diff --git a/doc/building.html b/doc/building.html index be3c8c364d7b..53defa0c61c8 100644 --- a/doc/building.html +++ b/doc/building.html @@ -146,6 +146,7 @@

Building the JDK

  • Virus Checking
  • Ccache
  • +
  • Sccache
  • Precompiled Headers
  • Icecc / @@ -365,11 +366,13 @@

    Building on aarch64

    also possible to use
    cross-compiling.

    Branch Protection

    In order to use Branch Protection features in the VM, ---enable-branch-protection must be used. This option -requires C++ compiler support (GCC 9.1.0+ or Clang 10+). The resulting -build can be run on both machines with and without support for branch -protection in hardware. Branch Protection is only supported for Linux -targets.

    +--enable-branch-protection must be used. This option requires C++ +compiler support for -mbranch-protection=standard in GCC 9.1.0+ or +Clang 10+ on Linux/AArch64 or for /guard:signret in Visual Studio +2019+ on Windows/ARM64. The resulting build can be run on both machines with and +without support for branch protection in hardware. Branch Protection is fully +supported for the Linux/AArch64 target and only partially supported for the +Windows/ARM64 targets.

    Building on 32-bit ARM

    This is not recommended. Instead, see the section on Cross-compiling.

    @@ -1814,6 +1817,14 @@

    Ccache

    often rebuild the same sources. Your mileage may vary however, so we recommend evaluating it for yourself. To enable it, make sure it's on the path and configure with --enable-ccache.

    +

    Sccache

    +

    The JDK build supports building with sccache when using gcc, clang, or +Microsoft toolchains. To enable it, make sure the sccache binary is on the path +(or specify the path to the binary using the SCCACHE argument to +the configure script) and configure with --enable-sccache. To +optionally specify where sccache stores its cache files, use +--with-sccache-dir. Precompiled headers are disabled when sccache +is enabled.

    Precompiled Headers

    By default, the Hotspot build uses pre-compiled headers (PCH) on the toolchains were it is properly supported (clang, gcc, and Visual diff --git a/doc/building.md b/doc/building.md index adf116764fa0..17eaf760b064 100644 --- a/doc/building.md +++ b/doc/building.md @@ -171,9 +171,11 @@ possible to use [cross-compiling](#cross-compiling). In order to use Branch Protection features in the VM, `--enable-branch-protection` must be used. This option requires C++ compiler -support (GCC 9.1.0+ or Clang 10+). The resulting build can be run on both -machines with and without support for branch protection in hardware. Branch -Protection is only supported for Linux targets. +support for `-mbranch-protection=standard` in GCC 9.1.0+ or Clang 10+ on +Linux/AArch64 or for `/guard:signret` in Visual Studio 2019+ on Windows/ARM64. +The resulting build can be run on both machines with and without support for +branch protection in hardware. Branch Protection is fully supported for the +Linux/AArch64 target and only partially supported for the Windows/ARM64 target. ### Building on 32-bit ARM @@ -1555,6 +1557,15 @@ the same sources. Your mileage may vary however, so we recommend evaluating it for yourself. To enable it, make sure it's on the path and configure with `--enable-ccache`. +### Sccache + +The JDK build supports building with sccache when using gcc, clang, or Microsoft +toolchains. To enable it, make sure the sccache binary is on the path (or +specify the path to the binary using the `SCCACHE` argument to the configure +script) and configure with `--enable-sccache`. To optionally specify where +sccache stores its cache files, use `--with-sccache-dir`. Precompiled headers +are disabled when sccache is enabled. + ### Precompiled Headers By default, the Hotspot build uses pre-compiled headers (PCH) on the toolchains diff --git a/make/Main.gmk b/make/Main.gmk index 198403844c31..a78fc509ff98 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -1420,6 +1420,9 @@ clean: $(CLEAN_DIR_TARGETS) clean-docs: $(call CleanDocs) +clean-microbenchmark: + $(call CleanMicrobenchmark) + clean-compile-commands: $(call CleanMakeSupportDir,compile-commands) @@ -1468,7 +1471,8 @@ dist-clean: clean ) $(ECHO) Cleaned everything, you will have to re-run configure. -ALL_TARGETS += clean clean-docs clean-compile-commands dist-clean $(CLEAN_DIR_TARGETS) \ +ALL_TARGETS += clean clean-docs clean-microbenchmark clean-compile-commands \ + dist-clean $(CLEAN_DIR_TARGETS) \ $(CLEAN_SUPPORT_DIR_TARGETS) $(CLEAN_TEST_TARGETS) $(CLEAN_PHASE_TARGETS) \ $(CLEAN_MODULE_TARGETS) $(CLEAN_MODULE_PHASE_TARGETS) diff --git a/make/MainSupport.gmk b/make/MainSupport.gmk index 6025cc74a942..f4bb403ab602 100644 --- a/make/MainSupport.gmk +++ b/make/MainSupport.gmk @@ -65,6 +65,14 @@ define CleanDocs @$(ECHO) " done" endef +define CleanMicrobenchmark + @$(PRINTF) "Cleaning microbenchmark build artifacts ..." + @$(ECHO) "" $(LOG_DEBUG) + $(RM) -r $(SUPPORT_OUTPUTDIR)/test/micro + $(RM) -r $(TEST_IMAGE_DIR)/micro + @$(ECHO) " done" +endef + # Cleans the dir given as $1 define CleanDir @$(PRINTF) "Cleaning %s build artifacts ..." "$(strip $1)" diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 1ae49298785e..1433ab32e5ce 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1054,9 +1054,11 @@ define SetupRunJtregTestBody $1_JTREG_BASIC_OPTIONS += -timeoutFactor:$$(JTREG_TIMEOUT_FACTOR) clean-outputdirs-$1: - $$(call LogWarn, Clean up dirs for $1) - $$(RM) -r $$($1_TEST_SUPPORT_DIR) - $$(RM) -r $$($1_TEST_RESULTS_DIR) + ifeq ($(JTREG_STATUS),) + $$(call LogWarn, Clean up dirs for $1) + $$(RM) -r $$($1_TEST_SUPPORT_DIR) + $$(RM) -r $$($1_TEST_RESULTS_DIR) + endif $1_COMMAND_LINE := \ $$(JTREG_JAVA) $$($1_JTREG_LAUNCHER_OPTIONS) \ diff --git a/make/autoconf/build-performance.m4 b/make/autoconf/build-performance.m4 index dfc9e979d2fd..88817c359695 100644 --- a/make/autoconf/build-performance.m4 +++ b/make/autoconf/build-performance.m4 @@ -252,6 +252,109 @@ AC_DEFUN([BPERF_SETUP_CCACHE_USAGE], fi ]) +AC_DEFUN([BPERF_SETUP_SCCACHE], +[ + # Check if sccache is available + SCCACHE_AVAILABLE=true + + UTIL_LOOKUP_TOOLCHAIN_PROGS(SCCACHE, sccache) + + AC_MSG_CHECKING([if sccache is available]) + if test "x$TOOLCHAIN_TYPE" != "xgcc" && test "x$TOOLCHAIN_TYPE" != "xclang" && \ + test "x$TOOLCHAIN_TYPE" != "xmicrosoft"; then + AC_MSG_RESULT([no, not supported for toolchain type $TOOLCHAIN_TYPE]) + SCCACHE_AVAILABLE=false + elif test "x$SCCACHE" = "x"; then + AC_MSG_RESULT([no, sccache binary missing or not executable]) + SCCACHE_AVAILABLE=false + else + AC_MSG_RESULT([yes]) + fi + + SCCACHE_STATUS="" + UTIL_ARG_ENABLE(NAME: sccache, DEFAULT: false, AVAILABLE: $SCCACHE_AVAILABLE, + DESC: [enable using sccache to speed up recompilations], + CHECKING_MSG: [if sccache is enabled], + IF_ENABLED: [ + if test "x$CCACHE" != x; then + AC_MSG_ERROR([Cannot enable both ccache and sccache]) + fi + # Versions of sccache before 0.10.0 can restore stale or incorrect + # dependency files for cached C/C++ compilations, breaking our build. + SCCACHE_VERSION=[`$SCCACHE --version | head -n1 | $CUT -d " " -f 2 | $TR -d '\r'`] + if test "x$SCCACHE_VERSION" = x; then + AC_MSG_ERROR([Could not determine sccache version]) + fi + HAS_BAD_SCCACHE=[`$ECHO $SCCACHE_VERSION | \ + $GREP -e '^0\.[0-9]\.' -e '^0\.[0-9]$'`] + if test "x$HAS_BAD_SCCACHE" != "x"; then + AC_MSG_ERROR([[sccache 0.10.0 or later is required, found $SCCACHE_VERSION]]) + fi + SCCACHE_STATUS="Active ($SCCACHE_VERSION)" + ], + IF_DISABLED: [ + SCCACHE="" + ]) + AC_SUBST(SCCACHE) + + AC_ARG_WITH([sccache-dir], + [AS_HELP_STRING([--with-sccache-dir], + [where to store sccache files @<:@~/.cache/sccache@:>@])]) + + if test "x$with_sccache_dir" != x; then + SCCACHE_DIR="$with_sccache_dir" + SCCACHE_DIR_FOR_SCCACHE="$SCCACHE_DIR" + + # Ideally, we'd use `UTIL_FIXUP_PATH()`, but it expects the supplied path to + # already exist, which might not be true for the sccache directory during + # the configure step. As a workaround, we manually invoke fixpath.sh. + if test "x$OPENJDK_BUILD_OS" = "xwindows"; then + SCCACHE_DIR_FOR_SCCACHE=`$FIXPATH_BASE -m print "$SCCACHE_DIR_FOR_SCCACHE"` + fi + + SET_SCCACHE_DIR="SCCACHE_DIR=$SCCACHE_DIR_FOR_SCCACHE" + if test "x$SCCACHE" = x; then + AC_MSG_WARN([--with-sccache-dir has no meaning when sccache is not enabled]) + fi + fi + + if test "x$SCCACHE" != x; then + BPERF_SETUP_SCCACHE_USAGE + fi +]) + +AC_DEFUN([BPERF_SETUP_SCCACHE_USAGE], +[ + if test "x$SCCACHE" != x; then + if test "x$USE_PRECOMPILED_HEADER" = "xtrue"; then + if test "x$PRECOMPILED_HEADERS_EXPLICITLY_SET" = "xtrue"; then + AC_MSG_ERROR([Cannot use sccache with precompiled headers. Use --disable-precompiled-headers.]) + else + AC_MSG_NOTICE([Disabling precompiled headers because sccache is enabled]) + USE_PRECOMPILED_HEADER=false + fi + fi + + # On Windows, the sccache binary must be launched through fixpath and the + # compiler argument passed to sccache must be the actual compiler + # (gcc/clang/cl) and not another fixpath invocation, otherwise sccache will + # try to execute fixpath as the compiler. + [ if [[ "$OPENJDK_BUILD_OS" = "windows" && "$SCCACHE" =~ ^"$FIXPATH " ]]; then ] + [ if [[ "$CC" =~ ^"$FIXPATH " ]]; then ] + CC="${CC#"$FIXPATH "}" + [ fi ] + [ if [[ "$CXX" =~ ^"$FIXPATH " ]]; then ] + CXX="${CXX#"$FIXPATH "}" + [ fi ] + [ fi ] + + if test "x$SET_SCCACHE_DIR" != x; then + SCCACHE="$SET_SCCACHE_DIR $SCCACHE" + mkdir -p "$SCCACHE_DIR" > /dev/null 2>&1 + fi + fi +]) + ################################################################################ # # Runs icecc-create-env once and prints the error if it fails @@ -372,7 +475,13 @@ AC_DEFUN_ONCE([BPERF_SETUP_PRECOMPILED_HEADERS], UTIL_ARG_ENABLE(NAME: precompiled-headers, DEFAULT: auto, RESULT: USE_PRECOMPILED_HEADER, AVAILABLE: $PRECOMPILED_HEADERS_AVAILABLE, - DESC: [enable using precompiled headers when compiling C++]) + DESC: [enable using precompiled headers when compiling C++], + IF_GIVEN: [ + PRECOMPILED_HEADERS_EXPLICITLY_SET=true + ], + IF_NOT_GIVEN: [ + PRECOMPILED_HEADERS_EXPLICITLY_SET=false + ]) AC_SUBST(USE_PRECOMPILED_HEADER) ]) diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 59380cdfe444..87c5f53a1e96 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -293,6 +293,9 @@ BPERF_SETUP_PRECOMPILED_HEADERS # Setup use of ccache, if available BPERF_SETUP_CCACHE +# Setup use of sccache, if available +BPERF_SETUP_SCCACHE + ################################################################################ # # And now the finish... diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 423654cd50ad..8f8a7af47fca 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -945,11 +945,21 @@ AC_DEFUN_ONCE([FLAGS_SETUP_BRANCH_PROTECTION], [ # Is branch protection available? BRANCH_PROTECTION_AVAILABLE=false - BRANCH_PROTECTION_FLAG="-mbranch-protection=standard" + BRANCH_PROTECTION_CFLAG="" + BRANCH_PROTECTION_ASFLAG="" if test "x$OPENJDK_TARGET_CPU" = xaarch64; then if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then - FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$BRANCH_PROTECTION_FLAG], + BRANCH_PROTECTION_CFLAG="-mbranch-protection=standard" + # The GCC/Clang assembler accepts the same flag as the compiler. + BRANCH_PROTECTION_ASFLAG="$BRANCH_PROTECTION_CFLAG" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$BRANCH_PROTECTION_CFLAG], + IF_TRUE: [BRANCH_PROTECTION_AVAILABLE=true]) + elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then + BRANCH_PROTECTION_CFLAG="/guard:signret" + # MSVC's assembler does not support branch protection flags, so + # BRANCH_PROTECTION_ASFLAG is intentionally left empty. + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$BRANCH_PROTECTION_CFLAG], IF_TRUE: [BRANCH_PROTECTION_AVAILABLE=true]) fi fi @@ -957,5 +967,5 @@ AC_DEFUN_ONCE([FLAGS_SETUP_BRANCH_PROTECTION], UTIL_ARG_ENABLE(NAME: branch-protection, DEFAULT: false, RESULT: BRANCH_PROTECTION_ENABLED, AVAILABLE: $BRANCH_PROTECTION_AVAILABLE, DESC: [enable branch protection when compiling C/C++], - IF_ENABLED: [BRANCH_PROTECTION_CFLAGS=$BRANCH_PROTECTION_FLAG]) + IF_ENABLED: [BRANCH_PROTECTION_CFLAGS=$BRANCH_PROTECTION_CFLAG]) ]) diff --git a/make/autoconf/flags-other.m4 b/make/autoconf/flags-other.m4 index 4570f6ede78a..df9fe66bb0d4 100644 --- a/make/autoconf/flags-other.m4 +++ b/make/autoconf/flags-other.m4 @@ -211,7 +211,7 @@ AC_DEFUN([FLAGS_SETUP_ASFLAGS_CPU_DEP], fi if test "x$BRANCH_PROTECTION_ENABLED" = "xtrue"; then - $2JVM_ASFLAGS="${$2JVM_ASFLAGS} $BRANCH_PROTECTION_FLAG" + $2JVM_ASFLAGS="${$2JVM_ASFLAGS} $BRANCH_PROTECTION_ASFLAG" fi AC_SUBST($2JVM_ASFLAGS) diff --git a/make/autoconf/help.m4 b/make/autoconf/help.m4 index d8c0b2ffaeff..23541cea3efa 100644 --- a/make/autoconf/help.m4 +++ b/make/autoconf/help.m4 @@ -335,6 +335,9 @@ AC_DEFUN_ONCE([HELP_PRINT_SUMMARY_AND_WARNINGS], if test "x$CCACHE_STATUS" != "x"; then $ECHO "* ccache status: $CCACHE_STATUS" fi + if test "x$SCCACHE_STATUS" != "x"; then + $ECHO "* sccache status: $SCCACHE_STATUS" + fi $ECHO "" if test "x$BUILDING_MULTIPLE_JVM_VARIANTS" = "xtrue"; then diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index 3779ec32e45f..1cfaf1186d50 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -545,7 +545,7 @@ ADLC_LANGSTD_CXXFLAGS := @ADLC_LANGSTD_CXXFLAGS@ ADLC_LDFLAGS := @ADLC_LDFLAGS@ # Tools that potentially need to be cross compilation aware. -CC := @CCACHE@ @ICECC@ @CC@ +CC := @SCCACHE@ @CCACHE@ @ICECC@ @CC@ # CFLAGS used to compile the jdk native libraries (C-code) CFLAGS_JDKLIB := @CFLAGS_JDKLIB@ @@ -571,7 +571,7 @@ EXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@ EXTRA_LDFLAGS := @EXTRA_LDFLAGS@ EXTRA_ASFLAGS := @EXTRA_ASFLAGS@ -CXX := @CCACHE@ @ICECC@ @CXX@ +CXX := @SCCACHE@ @CCACHE@ @ICECC@ @CXX@ CPP := @CPP@ @@ -740,6 +740,7 @@ RCFLAGS := @RCFLAGS@ AWK := @AWK@ BASENAME := @BASENAME@ CAT := @CAT@ +SCCACHE := @SCCACHE@ CCACHE := @CCACHE@ # CD is going away, but remains to cater for legacy makefiles. CD := cd diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 89f3dd63f426..0c7d4c272cde 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -457,17 +457,18 @@ class Address { Address(address target, relocInfo::relocType rtype = relocInfo::external_word_type); - Address(Register base, RegisterOrConstant index, extend ext = lsl()) { + Address(Register base, RegisterOrConstant index, extend ext = lsl(0)) { if (index.is_register()) { _mode = base_plus_offset_reg; new (&_nonliteral) Nonliteral(base, index.as_register(), 0, ext); } else { guarantee(ext.option() == ext::uxtx, "should be"); assert(index.is_constant(), "should be"); + assert(ext.shift() == 0, "must be"); _mode = base_plus_offset; new (&_nonliteral) Nonliteral(base, noreg, - index.as_constant() << ext.shift()); + index.as_constant()); } } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 4af590d51323..fba316bf293e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -182,10 +182,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Finish fast lock unsuccessfully. MUST branch to with flag == NE Label slow_path; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(t1, obj, rscratch2); @@ -245,60 +243,55 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, const ByteSize omc_monitor_offset = OMCache::monitor_offset(); const ByteSize omc_obj_offset = OMCache::obj_offset(); - if (!UseObjectMonitorTable) { - assert(t1_monitor == t1_mark, "should be the same here"); - } else { - const Register t1_hash = t1; - Label monitor_found; + const Register t1_hash = t1; + Label monitor_found; - // Save the mark, we might need it to extract the hash. - mov(t3, t1_mark); + // Save the mark, we might need it to extract the hash. + mov(t3, t1_mark); - // Look for the monitor in the current thread's object monitor cache (omc). + // Look for the monitor in the current thread's object monitor cache (omc). - ldr(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset)); - ldr(t2, Address(rthread, thr_omc_offset + omc_obj_offset)); - cmp(obj, t2); - br(Assembler::EQ, monitor_found); + ldr(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset)); + ldr(t2, Address(rthread, thr_omc_offset + omc_obj_offset)); + cmp(obj, t2); + br(Assembler::EQ, monitor_found); - // Look for the monitor in the table. + // Look for the monitor in the table. - // Get the hash code. - ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits); + // Get the hash code. + ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits); - // Get the table and calculate the bucket's address - lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address())); - ldr(t3, Address(t3)); - ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset())); - ands(t1_hash, t1_hash, t2); - ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset())); + // Get the table and calculate the bucket's address + lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address())); + ldr(t3, Address(t3)); + ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset())); + ands(t1_hash, t1_hash, t2); + ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset())); - // Read the monitor from the bucket. - ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord))); + // Read the monitor from the bucket. + ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord))); - // Check if the monitor in the bucket is special (empty, tombstone or removed). - cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special); - br(Assembler::LO, slow_path); + // Check if the monitor in the bucket is special (empty, tombstone or removed). + cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special); + br(Assembler::LO, slow_path); - // Check if object matches. - ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset())); - BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_peek_weak_handle_in_nmethod(this, t3, t3, t2, slow_path); - cmp(t3, obj); - br(Assembler::NE, slow_path); + // Check if object matches. + ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_peek_weak_handle_in_nmethod(this, t3, t3, t2, slow_path); + cmp(t3, obj); + br(Assembler::NE, slow_path); - // Store the monitor in the current thread's object monitor cache (omc). - str(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset)); - str(obj, Address(rthread, thr_omc_offset + omc_obj_offset)); + // Store the monitor in the current thread's object monitor cache (omc). + str(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset)); + str(obj, Address(rthread, thr_omc_offset + omc_obj_offset)); - bind(monitor_found); - } + bind(monitor_found); const Register t2_owner_addr = t2; const Register t3_owner = t3; - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address owner_address(t1_monitor, ObjectMonitor::owner_offset() - monitor_tag); - const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset() - monitor_tag); + const Address owner_address(t1_monitor, ObjectMonitor::owner_offset()); + const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset()); Label monitor_locked; @@ -318,10 +311,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, increment(recursions_address, 1); bind(monitor_locked); - if (UseObjectMonitorTable) { - // Cache the monitor for unlock. - str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - } + // Cache the monitor for unlock. + str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); } bind(locked); @@ -388,7 +379,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1, // Because we got here by popping (meaning we pushed in locked) // there will be no monitor in the box. So we need to push back the obj // so that the runtime can fix any potential anonymous owner. - tbnz(t1_mark, exact_log2(markWord::monitor_value), UseObjectMonitorTable ? push_and_slow_path : inflated); + tbnz(t1_mark, exact_log2(markWord::monitor_value), push_and_slow_path); // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); @@ -430,17 +421,10 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1, const Register t1_monitor = t1; - if (!UseObjectMonitorTable) { - assert(t1_monitor == t1_mark, "should be the same here"); - - // Untag the monitor. - add(t1_monitor, t1_mark, -(int)markWord::monitor_value); - } else { - ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*) - cmp(t1_monitor, checked_cast(alignof(ObjectMonitor*))); - br(Assembler::LO, slow_path); - } + ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*) + cmp(t1_monitor, checked_cast(alignof(ObjectMonitor*))); + br(Assembler::LO, slow_path); const Register t2_recursions = t2; Label not_recursive; @@ -2452,7 +2436,8 @@ void C2_MacroAssembler::neon_reverse_bytes(FloatRegister dst, FloatRegister src, void C2_MacroAssembler::neon_rearrange_hsd(FloatRegister dst, FloatRegister src, FloatRegister shuffle, FloatRegister tmp, BasicType bt, bool isQ) { - assert_different_registers(dst, src, shuffle, tmp); + assert_different_registers(dst, src, tmp); + assert_different_registers(shuffle, tmp); SIMD_Arrangement size1 = isQ ? T16B : T8B; SIMD_Arrangement size2 = esize2arrangement((uint)type2aelembytes(bt), isQ); @@ -3032,4 +3017,4 @@ void C2_MacroAssembler::sve_sdiv_short(FloatRegister dst_src1, FloatRegister src sve_sdiv(src1, S, ptrue, vtmp2); // Narrow the two INT result halves back to SHORT. sve_uzp1(dst_src1, H, vtmp1, src1); -} \ No newline at end of file +} diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index bbfd85282993..cc8d8b82e7b0 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -805,8 +805,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp } bool frame::was_augmented_on_entry(int& real_size) const { - assert(is_compiled_frame(), ""); - if (_cb->as_nmethod_or_null()->needs_stack_repair()) { + assert(_cb != nullptr && _cb->is_nmethod(), ""); + if (_cb->as_nmethod()->needs_stack_repair()) { // The stack increment resides just below the saved FP on the stack and // records the total frame size excluding the two words for saving FP and LR // (see MacroAssembler::remove_frame). diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index cf5ccb2c2863..78f8a86dcf34 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2412,16 +2412,6 @@ void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) { // load mark word ldr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes())); - if (!UseObjectMonitorTable) { - Label test_mark_word; - // check displaced - tst(temp_reg, markWord::unlocked_value); - br(Assembler::NE, test_mark_word); - // slow path use klass prototype - load_prototype_header(temp_reg, oop); - - bind(test_mark_word); - } andr(temp_reg, temp_reg, test_bit); if (jmp_set) { cbnz(temp_reg, jmp_label); @@ -7901,10 +7891,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R // instruction emitted as it is part of C1's null check semantics. ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())))); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())))); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(t1, obj, rscratch1); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 1d31777ad0cb..8f2eb05cbd5a 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -3180,13 +3180,9 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op } else { Register tmp1 = op->tmp1()->as_register(); Register tmp2 = op->tmp2()->as_register(); - if (left == right) { // same operand, so clearly the same klasses, let's save the check - __ b(*op->stub()->entry()); // -> do slow check - } else { - __ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2); - __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal), - *op->stub()->entry()); // same klass -> do slow check - } + __ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2); + __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal), + *op->stub()->entry()); // same klass -> do slow check // fall through to L_oops_not_equal } diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index 3803be5fddb1..69e164cfa87b 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -505,8 +505,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp } bool frame::was_augmented_on_entry(int& real_size) const { - assert(is_compiled_frame(), ""); - if (_cb->as_nmethod_or_null()->needs_stack_repair()) { + assert(_cb != nullptr && _cb->is_nmethod(), ""); + if (_cb->as_nmethod()->needs_stack_repair()) { Unimplemented(); } real_size = _cb->frame_size(); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index fe0ac25f58c3..e7bf14dad340 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -2688,7 +2688,6 @@ void MacroAssembler::tlab_allocate( void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register obj, Register box, Register tmp1, Register tmp2, Register tmp3) { assert_different_registers(obj, box, tmp1, tmp2, tmp3); - assert(UseObjectMonitorTable || tmp3 == noreg, "tmp3 not needed"); assert(flag == CR0, "bad condition register"); // Handle inflated monitor. @@ -2698,11 +2697,9 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // Finish fast lock unsuccessfully. MUST branch to with flag == EQ Label slow_path; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - li(tmp1, 0); - std(tmp1, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + li(tmp1, 0); + std(tmp1, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); @@ -2760,9 +2757,9 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // mark contains the tagged ObjectMonitor*. const uintptr_t monitor_tag = markWord::monitor_value; - const Register monitor = UseObjectMonitorTable ? tmp1 : noreg; + const Register monitor = tmp1; const Register owner_addr = tmp2; - const Register thread_id = UseObjectMonitorTable ? tmp3 : tmp1; + const Register thread_id = tmp3; // Offsets into the current thread's object monitor cache (omc). const ByteSize thr_omc_offset = JavaThread::om_cache_offset(); const ByteSize omc_monitor_offset = OMCache::monitor_offset(); @@ -2770,61 +2767,55 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register Label monitor_locked; - if (!UseObjectMonitorTable) { - // Compute owner address. - addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag); - mark = noreg; - } else { - const Register tmp3_bucket = tmp3; - const Register tmp2_hash = tmp2; - Label monitor_found; + const Register tmp3_bucket = tmp3; + const Register tmp2_hash = tmp2; + Label monitor_found; - // Save the mark, we might need it to extract the hash. - mr(tmp2_hash, mark); + // Save the mark, we might need it to extract the hash. + mr(tmp2_hash, mark); - // Look for the monitor in the current thread's object monitor cache (omc). + // Look for the monitor in the current thread's object monitor cache (omc). - ld(R0, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread); - ld(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread); - cmpd(CR0, R0, obj); - beq(CR0, monitor_found); + ld(R0, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread); + ld(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread); + cmpd(CR0, R0, obj); + beq(CR0, monitor_found); - // Look for the monitor in the table. + // Look for the monitor in the table. - // Get the hash code. - srdi(tmp2_hash, tmp2_hash, markWord::hash_shift); + // Get the hash code. + srdi(tmp2_hash, tmp2_hash, markWord::hash_shift); - // Get the table and calculate the bucket's address - int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true); - ld_ptr(tmp3, simm16_rest, tmp3); - ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3); - andr(tmp2_hash, tmp2_hash, tmp1); - ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3); + // Get the table and calculate the bucket's address + int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true); + ld_ptr(tmp3, simm16_rest, tmp3); + ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3); + andr(tmp2_hash, tmp2_hash, tmp1); + ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3); - // Read the monitor from the bucket. - sldi(tmp2_hash, tmp2_hash, LogBytesPerWord); - ldx(monitor, tmp3_bucket, tmp2_hash); + // Read the monitor from the bucket. + sldi(tmp2_hash, tmp2_hash, LogBytesPerWord); + ldx(monitor, tmp3_bucket, tmp2_hash); - // Check if the monitor in the bucket is special (empty, tombstone or removed). - cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); - blt(CR0, slow_path); + // Check if the monitor in the bucket is special (empty, tombstone or removed). + cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + blt(CR0, slow_path); - // Check if object matches. - ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor); - BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path); - cmpd(CR0, tmp3, obj); - bne(CR0, slow_path); + // Check if object matches. + ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path); + cmpd(CR0, tmp3, obj); + bne(CR0, slow_path); - // Store the monitor in the current thread's object monitor cache (omc). - std(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread); - std(obj, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread); + // Store the monitor in the current thread's object monitor cache (omc). + std(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread); + std(obj, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread); - bind(monitor_found); + bind(monitor_found); - // Compute owner address. - addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset())); - } + // Compute owner address. + addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset())); // Try to CAS owner (no owner => current thread's _monitor_owner_id). assert_different_registers(thread_id, monitor, owner_addr, box, R0); @@ -2843,23 +2834,14 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register bne(CR0, slow_path); // Recursive. - if (!UseObjectMonitorTable) { - assert_different_registers(tmp1, owner_addr); - ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr); - addi(tmp1, tmp1, 1); - std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr); - } else { - assert_different_registers(tmp2, monitor); - ld(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); - addi(tmp2, tmp2, 1); - std(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); - } + assert_different_registers(tmp2, monitor); + ld(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); + addi(tmp2, tmp2, 1); + std(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor); bind(monitor_locked); - if (UseObjectMonitorTable) { - // Cache the monitor for unlock. - std(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); - } + // Cache the monitor for unlock. + std(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); } bind(locked); @@ -2926,11 +2908,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe // Check for monitor (0b10). ld(mark, oopDesc::mark_offset_in_bytes(), obj); andi_(t, mark, markWord::monitor_value); - if (!UseObjectMonitorTable) { - bne(CR0, inflated); - } else { - bne(CR0, push_and_slow); - } + bne(CR0, push_and_slow); #ifdef ASSERT // Check header not unlocked (0b01). @@ -2980,15 +2958,10 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe const Register monitor = mark; const uintptr_t monitor_tag = markWord::monitor_value; - if (!UseObjectMonitorTable) { - // Untag the monitor. - subi(monitor, mark, monitor_tag); - } else { - ld(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); - // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*) - cmpldi(CR0, monitor, checked_cast(alignof(ObjectMonitor*))); - blt(CR0, slow_path); - } + ld(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box); + // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*) + cmpldi(CR0, monitor, checked_cast(alignof(ObjectMonitor*))); + blt(CR0, slow_path); const Register recursions = tmp2; Label not_recursive; @@ -3359,16 +3332,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int Label& jmp_label, bool maybe_far) { // load mark word ld(temp_reg, oopDesc::mark_offset_in_bytes(), oop); - if (!UseObjectMonitorTable) { - Label test_mark_word; - // if unlocked bit is set we can directly use the mark word - andi_(R0, temp_reg, markWord::unlocked_value); - bne(CR0, test_mark_word); - // slow path use klass prototype - load_prototype_header(temp_reg, oop); - - bind(test_mark_word); - } andi_(R0, temp_reg, test_bit); if (maybe_far) { bc_far_optimized(jmp_set ? Assembler::bcondCRbiIs0 : Assembler::bcondCRbiIs1, @@ -4907,11 +4870,9 @@ void MacroAssembler::fast_lock(Register box, Register obj, Register t1, Register Label push; const Register t = R0; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - li(t, 0); - std(t, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + li(t, 0); + std(t, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(t1, obj); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 8d9bf3ab0b2e..3681165812a5 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -10985,24 +10985,7 @@ instruct partialSubtypeCheckConstSuper(rarg3RegP sub, rarg2RegP super_reg, immP // inlined locking and unlocking -instruct cmpFastLock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2) %{ - predicate(!UseObjectMonitorTable); - match(Set crx (FastLock oop box)); - effect(TEMP tmp1, TEMP tmp2); - - format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2" %} - ins_encode %{ - __ fast_lock($crx$$CondRegister, $oop$$Register, $box$$Register, - $tmp1$$Register, $tmp2$$Register, noreg /*tmp3*/); - // If locking was successful, crx should indicate 'EQ'. - // The compiler generates a branch to the runtime call to - // _complete_monitor_locking_Java for the case where crx is 'NE'. - %} - ins_pipe(pipe_class_compare); -%} - -instruct cmpFastLockMonitorTable(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, flagsRegCR1 cr1) %{ - predicate(UseObjectMonitorTable); +instruct cmpFastLock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, flagsRegCR1 cr1) %{ match(Set crx (FastLock oop box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr1); diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 6b5f1bc70a74..364ed7de3b07 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -2525,8 +2525,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // Try fastpath for locking. // fast_lock kills r_temp_1, r_temp_2, r_temp_3. - Register r_temp_3_or_noreg = UseObjectMonitorTable ? r_temp_3 : noreg; - __ compiler_fast_lock_object(CR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3_or_noreg); + __ compiler_fast_lock_object(CR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); __ beq(CR0, locked); // None of the above fast optimizations worked so we have to get into the diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 5d174e77f37a..496e26d3c0b5 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -36,6 +36,7 @@ #include "ci/ciInlineKlass.hpp" #include "ci/ciInstance.hpp" #include "ci/ciObjArrayKlass.hpp" +#include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "gc/shared/collectedHeap.hpp" #include "nativeInst_riscv.hpp" @@ -43,6 +44,7 @@ #include "oops/oop.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/powerOfTwo.hpp" #include "vmreg_riscv.inline.hpp" @@ -441,6 +443,19 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod case T_LONG: assert(patch_code == lir_patch_none, "no patching handled here"); +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ la(dest->as_register_lo(), ExternalAddress(b)); + break; + } + if (AOTRuntimeConstants::contains(b)) { + __ load_aotrc_address(dest->as_register_lo(), b); + break; + } + } +#endif __ mv(dest->as_register_lo(), (intptr_t)c->as_jlong()); break; diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp index 4a915c727ae3..85e2e2866e9e 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp @@ -69,7 +69,7 @@ friend class ArrayCopyStub; _call_stub_size = 11 * MacroAssembler::instruction_size + 1 * MacroAssembler::instruction_size + wordSize, // See emit_exception_handler for detail - _exception_handler_size = DEBUG_ONLY(256) NOT_DEBUG(32), // or smaller + _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), // or smaller // See emit_deopt_handler for detail // far_call (2) + j (1) _deopt_handler_size = 1 * MacroAssembler::instruction_size + diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 011d20e2f9bf..b4277f729aee 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -84,10 +84,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, // Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0 Label slow_path; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - sd(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + sd(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); @@ -149,61 +147,56 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, const ByteSize omc_monitor_offset = OMCache::monitor_offset(); const ByteSize omc_obj_offset = OMCache::obj_offset(); - if (!UseObjectMonitorTable) { - assert(tmp1_monitor == tmp1_mark, "should be the same here"); - } else { - const Register tmp2_hash = tmp2; - const Register tmp3_bucket = tmp3; - Label monitor_found; + const Register tmp2_hash = tmp2; + const Register tmp3_bucket = tmp3; + Label monitor_found; - // Save the mark, we might need it to extract the hash. - mv(tmp2_hash, tmp1_mark); + // Save the mark, we might need it to extract the hash. + mv(tmp2_hash, tmp1_mark); - // Look for the monitor in the current thread's object monitor cache (omc). + // Look for the monitor in the current thread's object monitor cache (omc). - ld(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset)); - ld(tmp4, Address(xthread, thr_omc_offset + omc_obj_offset)); - beq(obj, tmp4, monitor_found); + ld(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset)); + ld(tmp4, Address(xthread, thr_omc_offset + omc_obj_offset)); + beq(obj, tmp4, monitor_found); - // Look for the monitor in the table. + // Look for the monitor in the table. - // Get the hash code. - srli(tmp2_hash, tmp2_hash, markWord::hash_shift); + // Get the hash code. + srli(tmp2_hash, tmp2_hash, markWord::hash_shift); - // Get the table and calculate the bucket's address. - la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address())); - ld(tmp3_t, Address(tmp3_t)); - ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset())); - andr(tmp2_hash, tmp2_hash, tmp1); - ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset())); + // Get the table and calculate the bucket's address. + la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address())); + ld(tmp3_t, Address(tmp3_t)); + ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset())); + andr(tmp2_hash, tmp2_hash, tmp1); + ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset())); - // Read the monitor from the bucket. - shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord); - ld(tmp1_monitor, Address(tmp3_bucket)); + // Read the monitor from the bucket. + shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord); + ld(tmp1_monitor, Address(tmp3_bucket)); - // Check if the monitor in the bucket is special (empty, tombstone or removed). - mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special); - bltu(tmp1_monitor, tmp2, slow_path); + // Check if the monitor in the bucket is special (empty, tombstone or removed). + mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special); + bltu(tmp1_monitor, tmp2, slow_path); - // Check if object matches. - ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset())); - BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path); - bne(tmp3, obj, slow_path); + // Check if object matches. + ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path); + bne(tmp3, obj, slow_path); - // Store the monitor in the current thread's object monitor cache (omc). - sd(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset)); - sd(obj, Address(xthread, thr_omc_offset + omc_obj_offset)); + // Store the monitor in the current thread's object monitor cache (omc). + sd(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset)); + sd(obj, Address(xthread, thr_omc_offset + omc_obj_offset)); - bind(monitor_found); - } + bind(monitor_found); const Register tmp2_owner_addr = tmp2; const Register tmp3_owner = tmp3; - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset() - monitor_tag); - const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag); + const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset()); + const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset()); Label monitor_locked; @@ -224,10 +217,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, increment(recursions_address, 1, tmp2, tmp3); bind(monitor_locked); - if (UseObjectMonitorTable) { - // Cache the monitor for unlock. - sd(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - } + // Cache the monitor for unlock. + sd(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); } bind(locked); @@ -300,7 +291,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, // there will be no monitor in the box. So we need to push back the obj // so that the runtime can fix any potential anonymous owner. test_bit(tmp3_t, tmp1_mark, exact_log2(markWord::monitor_value)); - bnez(tmp3_t, UseObjectMonitorTable ? push_and_slow_path : inflated); + bnez(tmp3_t, push_and_slow_path); // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); @@ -344,16 +335,10 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, const Register tmp1_monitor = tmp1; - if (!UseObjectMonitorTable) { - assert(tmp1_monitor == tmp1_mark, "should be the same here"); - // Untag the monitor. - subi(tmp1_monitor, tmp1_mark, (int)markWord::monitor_value); - } else { - ld(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - // No valid pointer below alignof(ObjectMonitor*). Take the slow path. - mv(tmp3_t, alignof(ObjectMonitor*)); - bltu(tmp1_monitor, tmp3_t, slow_path); - } + ld(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + // No valid pointer below alignof(ObjectMonitor*). Take the slow path. + mv(tmp3_t, alignof(ObjectMonitor*)); + bltu(tmp1_monitor, tmp3_t, slow_path); const Register tmp2_recursions = tmp2; Label not_recursive; @@ -1852,7 +1837,7 @@ void C2_MacroAssembler::arrays_hashcode_v(Register ary, Register cnt, Register r vsetvli(consumed, cnt, Assembler::e32, Assembler::m2); vle32_v(v_coeffs, t1); // 31^^(stride - 1) ... 31^^0 - vmv_v_x(v_sum, x0); + vmv_v_i(v_sum, 0); bind(VEC_LOOP); arrays_hashcode_elload_v(v_src, v_tmp, ary, eltype); @@ -2617,7 +2602,7 @@ void C2_MacroAssembler::java_round_float_v(VectorRegister dst, VectorRegister sr // replacing vfclass with feq as performance optimization vmfeq_vv(v0, src, src); // set dst = 0 in cases of NaN - vmv_v_x(dst, zr); + vmv_v_i(dst, 0); // dst = (src + 0.5) rounded down towards negative infinity vfadd_vf(dst, src, ftmp, Assembler::v0_t); @@ -2641,7 +2626,7 @@ void C2_MacroAssembler::java_round_double_v(VectorRegister dst, VectorRegister s // replacing vfclass with feq as performance optimization vmfeq_vv(v0, src, src); // set dst = 0 in cases of NaN - vmv_v_x(dst, zr); + vmv_v_i(dst, 0); // dst = (src + 0.5) rounded down towards negative infinity vfadd_vf(dst, src, ftmp, Assembler::v0_t); @@ -2699,7 +2684,7 @@ void C2_MacroAssembler::clear_array_v(Register base, Register cnt) { // making zero words vsetvli(t0, cnt, Assembler::e64, Assembler::m4); - vxor_vv(v4, v4, v4); + vmv_v_i(v4, 0); bind(loop); vsetvli(t0, cnt, Assembler::e64, Assembler::m4); @@ -3296,7 +3281,7 @@ void C2_MacroAssembler::integer_narrow_v(VectorRegister dst, BasicType dst_bt, u #define VFCVT_SAFE(VFLOATCVT) \ void C2_MacroAssembler::VFLOATCVT##_safe(VectorRegister dst, VectorRegister src) { \ assert_different_registers(dst, src); \ - vxor_vv(dst, dst, dst); \ + vmv_v_i(dst, 0); \ vmfeq_vv(v0, src, src); \ VFLOATCVT(dst, src, Assembler::v0_t); \ } diff --git a/src/hotspot/cpu/riscv/frame_riscv.cpp b/src/hotspot/cpu/riscv/frame_riscv.cpp index bf659c6053a0..20efa12d832f 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.cpp +++ b/src/hotspot/cpu/riscv/frame_riscv.cpp @@ -629,8 +629,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp } bool frame::was_augmented_on_entry(int& real_size) const { - assert(is_compiled_frame(), ""); - assert(!_cb->as_nmethod_or_null()->needs_stack_repair(), "unimplemented"); + assert(_cb != nullptr && _cb->is_nmethod(), ""); + assert(!_cb->as_nmethod()->needs_stack_repair(), "unimplemented"); real_size = _cb->frame_size(); return false; } diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp index bae1349ba14d..abcf69e2df09 100644 --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp @@ -24,6 +24,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" @@ -118,13 +119,20 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas // Iterate from start card to end card (inclusive). __ bind(loop); if (UseCondCardMark) { + // All non-clean cards (dirty, to-cset, from-remset) have bit0 == 0. + static_assert((G1CardTable::g1_dirty_card & 1U) == 0 + && (G1CardTable::g1_to_cset_card & 1U) == 0 + && (G1CardTable::g1_from_remset_card & 1U) == 0, + "cards needing scan must have bit0 == 0"); + // Clean card has bit0 == 1. + static_assert(((uint)G1CardTable::clean_card_val() & 1U) == 1, + "clean card must have bit0 == 1"); __ lbu(tmp, Address(start, 0)); - static_assert((uint)G1CardTable::clean_card_val() == 0xff, "must be"); - __ subi(tmp, tmp, G1CardTable::clean_card_val()); // Convert to clean_card_value() to a comparison - // against zero to avoid use of an extra temp. - __ bnez(tmp, next); + __ test_bit(tmp, tmp, 0); // test bit0: clean has bit0 == 1, non-clean has bit0 == 0 + __ beqz(tmp, next); // skip store if already non-clean } + // `sb zr` writes 0, which must be the dirty value. static_assert(G1CardTable::dirty_card_val() == 0, "must be to use zr"); __ sb(zr, Address(start, 0)); @@ -250,9 +258,22 @@ static void generate_post_barrier(MacroAssembler* masm, assert(thread == xthread, "must be"); assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg); // Does store cross heap regions? - __ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value - __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) - __ beqz(tmp1, done); +#if INCLUDE_CDS + // AOT code needs to load the barrier grain shift from the aot + // runtime constants area in the code cache otherwise we can compile + // it as an immediate operand + if (AOTCodeCache::is_on_for_dump()) { + __ xorr(tmp1, store_addr, new_val); + __ lwu(tmp2, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); + __ srl(tmp1, tmp1, tmp2); + __ beqz(tmp1, done); + } else +#endif + { + __ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value + __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes) + __ beqz(tmp1, done); + } // Crosses regions, storing null? if (new_val_may_be_null) { @@ -264,14 +285,23 @@ static void generate_post_barrier(MacroAssembler* masm, Address card_table_address(xthread, G1ThreadLocalData::card_table_base_offset()); __ ld(tmp2, card_table_address); // tmp2 := card table base address __ add(tmp1, tmp1, tmp2); // tmp1 := card address + if (UseCondCardMark) { - static_assert((uint)G1CardTable::clean_card_val() == 0xff, "must be"); + // All non-clean cards (dirty, to-cset, from-remset) have bit0 == 0. + static_assert((G1CardTable::g1_dirty_card & 1U) == 0 + && (G1CardTable::g1_to_cset_card & 1U) == 0 + && (G1CardTable::g1_from_remset_card & 1U) == 0, + "cards needing scan must have bit0 == 0"); + // Clean card has bit0 == 1. + static_assert(((uint)G1CardTable::clean_card_val() & 1U) == 1, + "clean card must have bit0 == 1"); __ lbu(tmp2, Address(tmp1, 0)); // tmp2 := card - __ subi(tmp2, tmp2, G1CardTable::clean_card_val()); // Convert to clean_card_value() to a comparison - // against zero to avoid use of an extra temp. - __ bnez(tmp2, done); + __ test_bit(tmp2, tmp2, 0); // test bit0: clean has bit0 == 1, non-clean has bit0 == 0 + __ beqz(tmp2, done); // skip store if already non-clean } - static_assert((uint)G1CardTable::dirty_card_val() == 0, "must be to use zr"); + + // `sb zr` writes 0, which must be the dirty value. + static_assert(G1CardTable::dirty_card_val() == 0, "must be to use zr"); __ sb(zr, Address(tmp1, 0)); } diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index 809221d66373..2139ffd52345 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -300,8 +300,10 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Label* slo // Because processors will not start the second load until the first comes back. // This means you can't overlap the two loads, // which is stronger than needed for ordering (stronger than TSO). - __ srli(ra, t0, 32); - __ orr(t1, t1, ra); + // XOR the guard into the epoch address twice. This preserves the + // address while making it dependent on the guard load. + __ xorr(t1, t1, t0); + __ xorr(t1, t1, t0); } // Read the global epoch value. __ lwu(t1, t1); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 6c39c8e456f1..647846d523b2 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -219,8 +219,14 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, // Test for in-cset if (is_strong) { - __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr()); - __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + if (AOTCodeCache::is_on_for_dump()) { + __ ld(t1, ExternalAddress(AOTRuntimeConstants::cset_base_address())); + __ lwu(t0, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); + __ srl(t0, x10, t0); + } else { + __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr()); + __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + } __ add(t1, t1, t0); __ lbu(t1, Address(t1)); __ test_bit(t0, t1, 0); @@ -815,8 +821,14 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { __ mv(_tmp2, _obj); } - __ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr()); - __ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + if (AOTCodeCache::is_on_for_dump()) { + __ lwu(_tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); + __ srl(_tmp2, _tmp2, _tmp1); + __ ld(_tmp1, ExternalAddress(AOTRuntimeConstants::cset_base_address())); + } else { + __ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr()); + __ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + } __ add(_tmp1, _tmp1, _tmp2); __ lbu(_tmp1, Address(_tmp1, 0)); maybe_far_jump_if_zero(masm, _tmp1); @@ -854,7 +866,7 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { // Save the result where needed. Narrow entries return narrowOop (32 bits) // we need to zero the upper 32 bits of x10. if (_narrow) { - __ zext_w(_obj, x10); + __ zext(_obj, x10, 32); } else { __ mv(_obj, x10); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 61b9633a4ef7..15d853f919b5 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -26,6 +26,7 @@ #include "asm/assembler.hpp" #include "asm/assembler.inline.hpp" +#include "cds/archiveBuilder.hpp" #include "ci/ciInlineKlass.hpp" #include "code/compiledIC.hpp" #include "compiler/disassembler.hpp" @@ -879,9 +880,13 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis } void MacroAssembler::stop(const char* msg) { - BLOCK_COMMENT(msg); + // Skip AOT caching C strings in scratch buffer. + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + BLOCK_COMMENT(str); + // load msg into a0 so we can access it from the signal handler + // ExternalAddress enables saving and restoring via the code cache + la(c_rarg0, ExternalAddress((address) str)); illegal_instruction(Assembler::csr::time); - emit_int64((uintptr_t)msg); } void MacroAssembler::unimplemented(const char* what) { @@ -911,10 +916,8 @@ void MacroAssembler::emit_static_call_stub() { void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments, Label *retaddr) { - int32_t offset = 0; push_reg(RegSet::of(t1, xmethod), sp); // push << t1 & xmethod >> to sp - movptr(t1, entry_point, offset, t0); - jalr(t1, offset); + rt_call(entry_point, t1, t0); if (retaddr != nullptr) { bind(*retaddr); } @@ -1164,16 +1167,17 @@ void MacroAssembler::jalr(Register Rs, int32_t offset) { Assembler::jalr(x1, Rs, offset); } -void MacroAssembler::rt_call(address dest, Register tmp) { - assert(tmp != x5, "tmp register must not be x5."); +void MacroAssembler::rt_call(address dest, Register tmp1, Register tmp2) { + assert_different_registers(tmp1, x5); + assert_different_registers(tmp1, tmp2); RuntimeAddress target(dest); if (CodeCache::contains(dest)) { - far_call(target, tmp); + far_call(target, tmp1); } else { relocate(target.rspec(), [&] { int32_t offset; - movptr(tmp, target.target(), offset); - jalr(tmp, offset); + movptr(tmp1, target.target(), offset, tmp2); + jalr(tmp1, offset); }); } } @@ -2182,7 +2186,7 @@ void MacroAssembler::vector_update_crc32(Register crc, Register buf, Register le vsetivli(zr, N, Assembler::e32, Assembler::m1, Assembler::mu, Assembler::tu); } - vmv_v_x(vcrc, zr); + vmv_v_i(vcrc, 0); vmv_s_x(vcrc, crc); // multiple of 64 @@ -2327,7 +2331,7 @@ void MacroAssembler::kernel_crc32_vclmul_fold_vectorsize_16(Register crc, Regist vle64_v(v6, buf); addi(buf, buf, STEP); vle64_v(v7, buf); addi(buf, buf, STEP); - vmv_v_x(v31, zr); + vmv_v_i(v31, 0); vsetivli(zr, 1, Assembler::e32, Assembler::m1, Assembler::mu, Assembler::tu); vmv_s_x(v31, crc); vsetivli(zr, N, Assembler::e64, Assembler::m1, Assembler::mu, Assembler::tu); @@ -2450,7 +2454,7 @@ void MacroAssembler::kernel_crc32_vclmul_fold_vectorsize_32(Register crc, Regist // now, v1 should contains: 010101... // initial crc - vmv_v_x(v24, zr); + vmv_v_i(v24, 0); vsetivli(zr, 1, Assembler::e32, Assembler::m4, Assembler::mu, Assembler::tu); vmv_s_x(v24, crc); vsetivli(zr, N, Assembler::e64, Assembler::m4, Assembler::mu, Assembler::tu); @@ -3054,7 +3058,7 @@ int MacroAssembler::patch_oop(address insn_addr, address o) { void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { - if (Universe::is_fully_initialized()) { + if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) { mv(xheapbase, CompressedOops::base()); } else { ld(xheapbase, ExternalAddress(CompressedOops::base_addr())); @@ -3765,16 +3769,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int assert_different_registers(temp_reg, t0); // load mark word ld(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes())); - if (!UseObjectMonitorTable) { - Label test_mark_word; - // check displaced - test_bit(t0, temp_reg, exact_log2(markWord::unlocked_value)); - bnez(t0, test_mark_word); - // slow path use klass prototype - load_prototype_header(temp_reg, oop); - - bind(test_mark_word); - } andi(temp_reg, temp_reg, tst_bit); if (jmp_set) { bnez(temp_reg, jmp_label, /* is_far */ true); @@ -3935,19 +3929,28 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register assert_different_registers(dst, tmp); assert_different_registers(src, tmp); - if (CompressedKlassPointers::base() == nullptr) { + Register xbase = tmp; + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically, we cannot just bake it in as immediate. + // Note that we only need to do this for base. The encoding shift would be the + // same between build time and runtime: the standard precomputed shift. + assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(), + "unexpected compressed klass shift!"); + ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr())); + } else if (CompressedKlassPointers::base() == nullptr) { if (CompressedKlassPointers::shift() != 0) { slli(dst, src, CompressedKlassPointers::shift()); } else { mv(dst, src); } return; + } else { + mv(xbase, (uintptr_t)CompressedKlassPointers::base()); } - Register xbase = tmp; - - mv(xbase, (uintptr_t)CompressedKlassPointers::base()); - if (CompressedKlassPointers::shift() != 0) { // dst = (src << shift) + xbase shadd(dst, src, xbase, dst /* temporary, dst != xbase */, CompressedKlassPointers::shift()); @@ -3962,6 +3965,28 @@ void MacroAssembler::encode_klass_not_null(Register r, Register tmp) { } void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) { + Register xbase = dst; + if (dst == src) { + xbase = tmp; + } + + if (AOTCodeCache::is_on_for_dump()) { + // We are generating code during AOT buildup that will run in *future* processes + // with likely different encoding settings. Therefore, we have to load the + // encoding base dynamically and must not take the base-value dependent zext + // short cut below. Note that we only need to do this for base; the encoding + // shift is the same at build and run time: the standard precomputed shift. + assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(), + "unexpected compressed klass shift!"); + assert_different_registers(src, xbase); + ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr())); + sub(dst, src, xbase); + if (CompressedKlassPointers::shift() != 0) { + srli(dst, dst, CompressedKlassPointers::shift()); + } + return; + } + if (CompressedKlassPointers::base() == nullptr) { if (CompressedKlassPointers::shift() != 0) { srli(dst, src, CompressedKlassPointers::shift()); @@ -3977,11 +4002,6 @@ void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register return; } - Register xbase = dst; - if (dst == src) { - xbase = tmp; - } - assert_different_registers(src, xbase); mv(xbase, (uintptr_t)CompressedKlassPointers::base()); sub(dst, src, xbase); @@ -5363,8 +5383,7 @@ void MacroAssembler::get_thread(Register thread) { RegSet::range(x28, x31) + ra - thread; push_reg(saved_regs, sp); - mv(t1, CAST_FROM_FN_PTR(address, Thread::current)); - jalr(t1); + rt_call(CAST_FROM_FN_PTR(address, Thread::current), t1, t0); if (thread != c_rarg0) { mv(thread, c_rarg0); } @@ -5374,12 +5393,33 @@ void MacroAssembler::get_thread(Register thread) { } void MacroAssembler::load_byte_map_base(Register reg) { +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address(); + ld(reg, ExternalAddress(byte_map_base_adr)); + return; + } +#endif CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); // Strictly speaking the card table base isn't an address at all, and it might // even be negative. It is thus materialised as a constant. mv(reg, (uint64_t)ctbs->card_table_base_const()); } +void MacroAssembler::load_aotrc_address(Register reg, address a) { +#if INCLUDE_CDS + assert(AOTRuntimeConstants::contains(a), "address out of range for data area"); + if (AOTCodeCache::is_on_for_dump()) { + // all aotrc field addresses should be registered in the AOTCodeCache address table + la(reg, ExternalAddress(a)); + } else { + mv(reg, (intptr_t)a); + } +#else + ShouldNotReachHere(); +#endif +} + void MacroAssembler::build_frame(int framesize) { assert(framesize >= 2, "framesize must include space for FP/RA"); assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment"); @@ -7035,10 +7075,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register tmp1, // instruction emitted as it is part of C1's null check semantics. ld(mark, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - sd(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())))); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + sd(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())))); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index b7d493fe8908..dffb85455f0b 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -28,6 +28,7 @@ #define CPU_RISCV_MACROASSEMBLER_RISCV_HPP #include "asm/assembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/vmreg.hpp" #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" @@ -770,7 +771,7 @@ class MacroAssembler: public Assembler { // is used to keep the entry address for jalr/movptr. // Uses call() for intra code cache, else movptr + jalr. // Clobebrs t1 - void rt_call(address dest, Register tmp = t1); + void rt_call(address dest, Register tmp1 = t1, Register tmp2 = noreg); // ret: jalr x0, 0(x1) inline void ret() { @@ -1291,6 +1292,9 @@ class MacroAssembler: public Assembler { void load_byte_map_base(Register reg); + // Load a constant address in the AOT Runtime Constants area + void load_aotrc_address(Register reg, address a); + void bang_stack_with_offset(int offset) { // stack grows down, caller passes positive offset assert(offset > 0, "must bang with negative offset"); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 43590e89c521..033e4a4222e0 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1553,9 +1553,34 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r int src_offset = ra_->reg2offset(src_lo); int dst_offset = ra_->reg2offset(dst_lo); - if (bottom_type()->isa_vect() != nullptr) { - uint ireg = ideal_reg(); - if (ireg == Op_VecA && masm) { + // Stack-to-stack copies use t0 for the value. Bail out if a destination + // address also needs t0 to materialize an offset outside the 12-bit range. + if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { + int last_dst_offset = dst_offset; + if (bottom_type()->isa_vect() != nullptr) { + if (!bottom_type()->isa_pvectmask()) { + assert(ideal_reg() == Op_VecA, "Must be Op_VecA"); + int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); + last_dst_offset += vector_reg_size_in_bytes - 8; + } else { + assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask"); + int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8; + last_dst_offset += vmask_size_in_bytes - 4; + } + } + + if (masm != nullptr && !Assembler::is_simm12(last_dst_offset)) { + // size() emits into a scratch buffer where recording a failure is not allowed. + if (!C->output()->in_scratch_emit_size()) { + C->record_method_not_compilable("unsupported large stack-to-stack spill copy"); + } + return 0; + } + } + + if (bottom_type()->isa_vect() != nullptr && masm != nullptr) { + if (!bottom_type()->isa_pvectmask()) { + assert(ideal_reg() == Op_VecA, "Must be Op_VecA"); int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { // stack to stack @@ -1574,7 +1599,8 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r } else { ShouldNotReachHere(); } - } else if (bottom_type()->isa_pvectmask() && masm) { + } else { + assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask"); int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8; if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { // stack to stack @@ -1677,14 +1703,11 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r st->print("%s", Matcher::regName[dst_lo]); } if (bottom_type()->isa_vect() && !bottom_type()->isa_pvectmask()) { - int vsize = 0; - if (ideal_reg() == Op_VecA) { - vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8; - } else { - ShouldNotReachHere(); - } + assert(ideal_reg() == Op_VecA, "Must be Op_VecA"); + int vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8; st->print("\t# vector spill size = %d", vsize); - } else if (ideal_reg() == Op_RegVectMask) { + } else if (bottom_type()->isa_pvectmask()) { + assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask"); assert(Matcher::supports_scalable_vector(), "bad register type for spill"); int vsize = Matcher::scalable_predicate_reg_slots() * 32; st->print("\t# vmask spill size = %d", vsize); @@ -2400,10 +2423,7 @@ encode %{ // Make the anchor frame walkable __ la(t0, retaddr); __ sd(t0, Address(xthread, JavaThread::last_Java_pc_offset())); - int32_t offset = 0; - // No relocation needed - __ movptr(t1, entry, offset, t0); // lui + lui + slli + add - __ jalr(t1, offset); + __ rt_call(entry, t1, t0); __ bind(retaddr); __ post_call_nop(); } @@ -2800,6 +2820,18 @@ operand immP_1() interface(CONST_INTER); %} +// AOT Runtime Constants Address +operand immAOTRuntimeConstantsAddress() +%{ + // Check if the address is in the range of AOT Runtime Constants + predicate(AOTRuntimeConstants::contains((address)(n->get_ptr()))); + match(ConP); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // Int Immediate: low 16-bit mask operand immI_16bits() %{ @@ -4761,6 +4793,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con) ins_pipe(ialu_imm); %} +instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con) +%{ + match(Set dst con); + + ins_cost(ALU_COST); + format %{ "la $dst, $con\t# aotrc, #@loadAOTRCAddress" %} + + ins_encode %{ + __ load_aotrc_address($dst$$Register, (address)$con$$constant); + %} + + ins_pipe(ialu_imm); +%} + // Load Narrow Pointer Constant instruct loadConN(iRegNNoSp dst, immN con) %{ @@ -8531,6 +8577,157 @@ instruct convI2L_reg_reg(iRegLNoSp dst, iRegIorL2I src) ins_pipe(ialu_reg); %} +// Fused int-producer + ConvI2L rules. +instruct convI2L_addI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (AddI src1 src2))); + + ins_cost(ALU_COST); + format %{ "addw $dst, $src1, $src2\t#@convI2L_addI_reg_reg" %} + + ins_encode %{ + __ addw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +instruct convI2L_addI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immIAdd src2) %{ + match(Set dst (ConvI2L (AddI src1 src2))); + + ins_cost(ALU_COST); + format %{ "addiw $dst, $src1, $src2\t#@convI2L_addI_reg_imm" %} + + ins_encode %{ + __ addiw(as_Register($dst$$reg), + as_Register($src1$$reg), + $src2$$constant); + %} + + ins_pipe(ialu_reg_imm); +%} + +instruct convI2L_subI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (SubI src1 src2))); + + ins_cost(ALU_COST); + format %{ "subw $dst, $src1, $src2\t#@convI2L_subI_reg_reg" %} + + ins_encode %{ + __ subw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg); +%} + +instruct convI2L_mulI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (MulI src1 src2))); + + ins_cost(IMUL_COST); + format %{ "mulw $dst, $src1, $src2\t#@convI2L_mulI_reg_reg" %} + + ins_encode %{ + __ mulw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(imul_reg_reg); +%} + +instruct convI2L_lShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (LShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "sllw $dst, $src1, $src2\t#@convI2L_lShiftI_reg_reg" %} + + ins_encode %{ + __ sllw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg_vshift); +%} + +instruct convI2L_lShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{ + match(Set dst (ConvI2L (LShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "slliw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_lShiftI_reg_imm" %} + + ins_encode %{ + __ slliw(as_Register($dst$$reg), + as_Register($src1$$reg), + (unsigned) $src2$$constant & 0x1f); + %} + + ins_pipe(ialu_reg_shift); +%} + +instruct convI2L_urShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (URShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "srlw $dst, $src1, $src2\t#@convI2L_urShiftI_reg_reg" %} + + ins_encode %{ + __ srlw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg_vshift); +%} + +instruct convI2L_urShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{ + match(Set dst (ConvI2L (URShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "srliw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_urShiftI_reg_imm" %} + + ins_encode %{ + __ srliw(as_Register($dst$$reg), + as_Register($src1$$reg), + (unsigned) $src2$$constant & 0x1f); + %} + + ins_pipe(ialu_reg_shift); +%} + +instruct convI2L_rShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ + match(Set dst (ConvI2L (RShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "sraw $dst, $src1, $src2\t#@convI2L_rShiftI_reg_reg" %} + + ins_encode %{ + __ sraw(as_Register($dst$$reg), + as_Register($src1$$reg), + as_Register($src2$$reg)); + %} + + ins_pipe(ialu_reg_reg_vshift); +%} + +instruct convI2L_rShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{ + match(Set dst (ConvI2L (RShiftI src1 src2))); + + ins_cost(ALU_COST); + format %{ "sraiw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_rShiftI_reg_imm" %} + + ins_encode %{ + __ sraiw(as_Register($dst$$reg), + as_Register($src1$$reg), + (unsigned) $src2$$constant & 0x1f); + %} + + ins_pipe(ialu_reg_shift); +%} + instruct convL2I_reg(iRegINoSp dst, iRegL src) %{ match(Set dst (ConvL2I src)); diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 07ce0f7885a3..ef0ce89133ed 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -41,7 +41,7 @@ source %{ __ vsex_v(reg, base, sew, vm); } else { if (vm == Assembler::v0_t) { - __ vxor_vv(reg, reg, reg); + __ vmv_v_i(reg, 0); } __ vlex_v(reg, base, sew, vm); } @@ -241,7 +241,7 @@ instruct vstoremask(vReg dst, vRegMask_V0 v0, immI size) %{ format %{ "vstoremask $dst, V0 # elem size is $size byte[s]" %} ins_encode %{ __ vsetvli_helper(T_BOOLEAN, Matcher::vector_length(this)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vmerge_vim(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), 1); %} ins_pipe(pipe_slow); @@ -1580,6 +1580,42 @@ instruct vnotL_masked(vReg dst_src, immI_M1 m1, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} +// vector integer div + +instruct vdiv(vReg dst, vReg src1, vReg src2) %{ + match(Set dst (DivVB src1 src2)); + match(Set dst (DivVS src1 src2)); + match(Set dst (DivVI src1 src2)); + match(Set dst (DivVL src1 src2)); + format %{ "vdiv $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vdiv_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector integer div - predicated + +instruct vdiv_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ + match(Set dst_src1 (DivVB (Binary dst_src1 src2) v0)); + match(Set dst_src1 (DivVS (Binary dst_src1 src2) v0)); + match(Set dst_src1 (DivVI (Binary dst_src1 src2) v0)); + match(Set dst_src1 (DivVL (Binary dst_src1 src2) v0)); + format %{ "vdiv_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vdiv_vv(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_VectorRegister($src2$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + // vector float div instruct vdiv_hfp(vReg dst, vReg src1, vReg src2) %{ @@ -4482,7 +4518,7 @@ instruct vmaskAllL(vRegMask dst, iRegL src) %{ // ------------------------------ Vector mask basic OPs ------------------------ -// vector mask logical ops: and/or/xor +// vector mask logical ops: and/and-not/or/xor instruct vmask_and(vRegMask dst, vRegMask src1, vRegMask src2) %{ match(Set dst (AndVMask src1 src2)); @@ -4497,6 +4533,32 @@ instruct vmask_and(vRegMask dst, vRegMask src1, vRegMask src2) %{ ins_pipe(pipe_slow); %} +instruct vmask_and_notI(vRegMask dst, vRegMask src1, vRegMask src2, immI_M1 m1) %{ + match(Set dst (AndVMask src1 (XorVMask src2 (MaskAll m1)))); + format %{ "vmask_and_notI $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vmandn_mm(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vmask_and_notL(vRegMask dst, vRegMask src1, vRegMask src2, immL_M1 m1) %{ + match(Set dst (AndVMask src1 (XorVMask src2 (MaskAll m1)))); + format %{ "vmask_and_notL $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vmandn_mm(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + instruct vmask_or(vRegMask dst, vRegMask src1, vRegMask src2) %{ match(Set dst (OrVMask src1 src2)); format %{ "vmask_or $dst, $src1, $src2" %} @@ -4809,7 +4871,7 @@ instruct vcvtFtoL(vReg dst, vReg src, vRegMask_V0 v0) %{ format %{ "vcvtFtoL $dst, $src" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vsetvli_helper(T_FLOAT, Matcher::vector_length(this), Assembler::mf2); __ vmfeq_vv(as_VectorRegister($v0$$reg), as_VectorRegister($src$$reg), as_VectorRegister($src$$reg)); __ vfwcvt_rtz_x_f_v(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), Assembler::v0_t); @@ -4842,7 +4904,7 @@ instruct vcvtDtoX_narrow(vReg dst, vReg src, vRegMask_V0 v0) %{ __ vsetvli_helper(T_DOUBLE, Matcher::vector_length(this)); __ vmfeq_vv(as_VectorRegister($v0$$reg), as_VectorRegister($src$$reg), as_VectorRegister($src$$reg)); __ vsetvli_helper(T_INT, Matcher::vector_length(this), Assembler::mf2); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vfncvt_rtz_x_f_w(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), Assembler::v0_t); BasicType bt = Matcher::vector_element_basic_type(this); if (bt == T_BYTE || bt == T_SHORT) { @@ -4903,7 +4965,7 @@ instruct reinterpretResize(vReg dst, vReg src) %{ "invalid vector length"); BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vsetvli_helper(T_BYTE, length_in_bytes_resize); __ vmv_v_v(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg)); %} @@ -4931,7 +4993,7 @@ instruct vmask_reinterpret_diff_esize(vRegMask dst, vRegMask_V0 src, vReg tmp) % ins_encode %{ BasicType from_bt = Matcher::vector_element_basic_type(this, $src); __ vsetvli_helper(from_bt, Matcher::vector_length(this, $src)); - __ vxor_vv(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg)); + __ vmv_v_i(as_VectorRegister($tmp$$reg), 0); __ vmerge_vim(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), -1); BasicType to_bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(to_bt, Matcher::vector_length(this)); @@ -4990,8 +5052,7 @@ instruct rearrange_masked(vReg dst, vReg src, vReg shuffle, vRegMask_V0 v0) %{ ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), - as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vrgather_vv(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), as_VectorRegister($shuffle$$reg), Assembler::v0_t); %} @@ -5105,8 +5166,7 @@ instruct vcompress(vReg dst, vReg src, vRegMask_V0 v0) %{ ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), - as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vcompress_vm(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), as_VectorRegister($v0$$reg)); %} @@ -5121,8 +5181,7 @@ instruct vexpand(vReg dst, vReg src, vRegMask_V0 v0, vReg tmp) %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); __ viota_m(as_VectorRegister($tmp$$reg), as_VectorRegister($v0$$reg)); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), - as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vrgather_vv(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), as_VectorRegister($tmp$$reg), Assembler::v0_t); %} @@ -5383,8 +5442,7 @@ instruct gather_loadS_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, v Assembler::SEW sew = Assembler::elemtype_to_sew(bt); __ vsetvli_helper(bt, Matcher::vector_length(this)); __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg), (int)sew); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), - as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vluxei32_v(as_VectorRegister($dst$$reg), as_Register($mem$$base), as_VectorRegister($tmp$$reg), Assembler::v0_t); %} @@ -5402,8 +5460,7 @@ instruct gather_loadD_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, v __ vsetvli_helper(bt, Matcher::vector_length(this)); __ vzext_vf2(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg)); __ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew); - __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), - as_VectorRegister($dst$$reg)); + __ vmv_v_i(as_VectorRegister($dst$$reg), 0); __ vluxei64_v(as_VectorRegister($dst$$reg), as_Register($mem$$base), as_VectorRegister($tmp$$reg), Assembler::v0_t); %} diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp index c52d5a31066a..5a1fdbe773a1 100644 --- a/src/hotspot/cpu/riscv/runtime_riscv.cpp +++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp @@ -26,6 +26,7 @@ #ifdef COMPILER2 #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/vmreg.hpp" #include "interpreter/interpreter.hpp" #include "opto/runtime.hpp" @@ -58,10 +59,15 @@ class SimpleRuntimeFrame { //------------------------------generate_uncommon_trap_blob-------------------- UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { + const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id); + if (blob != nullptr) { + return blob->as_uncommon_trap_blob(); + } + // Allocate space for the code ResourceMark rm; // Setup code generation tools - const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); if (buffer.blob() == nullptr) { return nullptr; @@ -243,8 +249,10 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Make sure all code is generated masm->flush(); - return UncommonTrapBlob::create(&buffer, oop_maps, - SimpleRuntimeFrame::framesize >> 1); + UncommonTrapBlob* ut_blob = UncommonTrapBlob::create(&buffer, oop_maps, + SimpleRuntimeFrame::framesize >> 1); + AOTCodeCache::store_code_blob(*ut_blob, AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id); + return ut_blob; } //------------------------------generate_exception_blob--------------------------- @@ -278,10 +286,15 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); + const char* name = OptoRuntime::stub_name(StubId::c2_exception_id); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_exception_id); + if (blob != nullptr) { + return blob->as_exception_blob(); + } + // Allocate space for the code ResourceMark rm; // Setup code generation tools - const char* name = OptoRuntime::stub_name(StubId::c2_exception_id); CodeBuffer buffer(name, 2048, 1024); if (buffer.blob() == nullptr) { return nullptr; @@ -380,6 +393,8 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { masm->flush(); // Set exception blob - return ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); + ExceptionBlob* ex_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1); + AOTCodeCache::store_code_blob(*ex_blob, AOTCodeEntry::C2Blob, BlobId::c2_exception_id); + return ex_blob; } #endif // COMPILER2 diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index eee5184dfcac..ec0a5f5d9b3a 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -27,6 +27,7 @@ #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/symbolTable.hpp" +#include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "code/debugInfoRec.hpp" #include "code/vtableStubs.hpp" @@ -2123,6 +2124,12 @@ void SharedRuntime::generate_deopt_blob() { // Setup code generation tools int pad = 0; const char* name = SharedRuntime::stub_name(StubId::shared_deopt_id); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id); + if (blob != nullptr) { + _deopt_blob = blob->as_deoptimization_blob(); + return; + } + CodeBuffer buffer(name, 2048 + pad, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); int frame_size_in_words = -1; @@ -2427,6 +2434,8 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words); assert(_deopt_blob != nullptr, "create deoptimization blob fail!"); _deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset); + + AOTCodeCache::store_code_blob(*_deopt_blob, AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id); } // Number of stack slots between incoming argument block and the start of @@ -2453,13 +2462,18 @@ VMReg SharedRuntime::thread_register() { SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr) { assert(is_polling_page_id(id), "expected a polling page stub id"); + const char* name = SharedRuntime::stub_name(id); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id)); + if (blob != nullptr) { + return blob->as_safepoint_blob(); + } + ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); assert_cond(oop_maps != nullptr); OopMap* map = nullptr; // Allocate space for the code. Setup code generation tools. - const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); @@ -2564,7 +2578,10 @@ SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr) masm->flush(); // Fill-out other meta info - return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); + SafepointBlob* sp_blob = SafepointBlob::create(&buffer, oop_maps, frame_size_in_words); + + AOTCodeCache::store_code_blob(*sp_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id)); + return sp_blob; } // @@ -2579,10 +2596,15 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); assert(is_resolve_id(id), "expected a resolve stub id"); + const char* name = SharedRuntime::stub_name(id); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id)); + if (blob != nullptr) { + return blob->as_runtime_stub(); + } + // allocate space for the code ResourceMark rm; - const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); @@ -2653,7 +2675,10 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination masm->flush(); // return the blob - return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); + RuntimeStub* rs_blob = RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true); + + AOTCodeCache::store_code_blob(*rs_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id)); + return rs_blob; } // Continuation point for throwing of implicit exceptions that are @@ -2698,6 +2723,11 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_ const char* timer_msg = "SharedRuntime generate_throw_exception"; TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id)); + if (blob != nullptr) { + return blob->as_runtime_stub(); + } + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2756,6 +2786,8 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_ (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); assert(stub != nullptr, "create runtime stub fail!"); + + AOTCodeCache::store_code_blob(*stub, AOTCodeEntry::SharedBlob, StubInfo::blob(id)); return stub; } diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 7f43a1dba690..260d31fc7cdc 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -67,6 +67,109 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") +alignas(64) static const char _encodeBlock_toBase64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +alignas(64) static const char _encodeBlock_toBase64URL[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' +}; + +static const uint8_t _decodeBlock_fromBase64[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +static const uint8_t _decodeBlock_fromBase64URL[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +alignas(64) static const uint32_t _sha256_round_consts[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +alignas(64) static const uint64_t _sha512_round_consts[80] = { + 0x428a2f98d728ae22l, 0x7137449123ef65cdl, 0xb5c0fbcfec4d3b2fl, + 0xe9b5dba58189dbbcl, 0x3956c25bf348b538l, 0x59f111f1b605d019l, + 0x923f82a4af194f9bl, 0xab1c5ed5da6d8118l, 0xd807aa98a3030242l, + 0x12835b0145706fbel, 0x243185be4ee4b28cl, 0x550c7dc3d5ffb4e2l, + 0x72be5d74f27b896fl, 0x80deb1fe3b1696b1l, 0x9bdc06a725c71235l, + 0xc19bf174cf692694l, 0xe49b69c19ef14ad2l, 0xefbe4786384f25e3l, + 0x0fc19dc68b8cd5b5l, 0x240ca1cc77ac9c65l, 0x2de92c6f592b0275l, + 0x4a7484aa6ea6e483l, 0x5cb0a9dcbd41fbd4l, 0x76f988da831153b5l, + 0x983e5152ee66dfabl, 0xa831c66d2db43210l, 0xb00327c898fb213fl, + 0xbf597fc7beef0ee4l, 0xc6e00bf33da88fc2l, 0xd5a79147930aa725l, + 0x06ca6351e003826fl, 0x142929670a0e6e70l, 0x27b70a8546d22ffcl, + 0x2e1b21385c26c926l, 0x4d2c6dfc5ac42aedl, 0x53380d139d95b3dfl, + 0x650a73548baf63del, 0x766a0abb3c77b2a8l, 0x81c2c92e47edaee6l, + 0x92722c851482353bl, 0xa2bfe8a14cf10364l, 0xa81a664bbc423001l, + 0xc24b8b70d0f89791l, 0xc76c51a30654be30l, 0xd192e819d6ef5218l, + 0xd69906245565a910l, 0xf40e35855771202al, 0x106aa07032bbd1b8l, + 0x19a4c116b8d2d0c8l, 0x1e376c085141ab53l, 0x2748774cdf8eeb99l, + 0x34b0bcb5e19b48a8l, 0x391c0cb3c5c95a63l, 0x4ed8aa4ae3418acbl, + 0x5b9cca4f7763e373l, 0x682e6ff3d6b2b8a3l, 0x748f82ee5defb2fcl, + 0x78a5636f43172f60l, 0x84c87814a1f0ab72l, 0x8cc702081a6439ecl, + 0x90befffa23631e28l, 0xa4506cebde82bde9l, 0xbef9a3f7b2c67915l, + 0xc67178f2e372532bl, 0xca273eceea26619cl, 0xd186b8c721c0c207l, + 0xeada7dd6cde0eb1el, 0xf57d4f7fee6ed178l, 0x06f067aa72176fbal, + 0x0a637dc5a2c898a6l, 0x113f9804bef90dael, 0x1b710b35131c471bl, + 0x28db77f523047d84l, 0x32caab7b40c72493l, 0x3c9ebe0a15c9bebcl, + 0x431d67c49c100d4cl, 0x4cc5d4becb3e42b6l, 0x597f299cfc657e2al, + 0x5fcb6fab3ad6faecl, 0x6c44198c4a475817l +}; + // Stub Code definitions class StubGenerator: public StubCodeGenerator { @@ -208,8 +311,17 @@ class StubGenerator: public StubCodeGenerator { "adjust this code"); StubId stub_id = StubId::stubgen_call_stub_id; + GrowableArray

    entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, "expected 1 extra entry"); + return_address = entries.at(0); + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Address sp_after_call (fp, sp_after_call_off * wordSize); @@ -357,6 +469,7 @@ class StubGenerator: public StubCodeGenerator { // save current address for use by exception handling code return_address = __ pc(); + entries.append(return_address); // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) @@ -460,6 +573,9 @@ class StubGenerator: public StubCodeGenerator { __ fsd(j_farg0, Address(j_rarg2, 0), t0); __ j(exit); + // record the stub entry and end plus the auxiliary entry + store_archive_data(stub_id, start, __ pc(), &entries); + return start; } @@ -477,8 +593,14 @@ class StubGenerator: public StubCodeGenerator { address generate_catch_exception() { StubId stub_id = StubId::stubgen_catch_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_call_stub(): const Address thread(fp, thread_off * wordSize); @@ -501,7 +623,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(x10); __ sd(x10, Address(xthread, Thread::pending_exception_offset())); - __ mv(t0, (address)__FILE__); + // special case -- add file name string to AOT address table + address file = (address)AOTCodeCache::add_C_string(__FILE__); + __ la(t0, ExternalAddress(file)); __ sd(t0, Address(xthread, Thread::exception_file_offset())); __ mv(t0, (int)__LINE__); __ sw(t0, Address(xthread, Thread::exception_line_offset())); @@ -511,6 +635,9 @@ class StubGenerator: public StubCodeGenerator { "_call_stub_return_address must have been generated before"); __ j(RuntimeAddress(StubRoutines::_call_stub_return_address)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -530,8 +657,14 @@ class StubGenerator: public StubCodeGenerator { address generate_forward_exception() { StubId stub_id = StubId::stubgen_forward_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Upon entry, RA points to the return address returning into // Java (interpreted or compiled) code; i.e., the return address @@ -598,6 +731,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(x10); __ jr(x9); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -617,8 +753,14 @@ class StubGenerator: public StubCodeGenerator { address generate_verify_oop() { StubId stub_id = StubId::stubgen_verify_oop_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label exit, error; @@ -658,6 +800,9 @@ class StubGenerator: public StubCodeGenerator { __ rt_call(CAST_FROM_FN_PTR(address, MacroAssembler::debug64)); __ ebreak(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -673,14 +818,20 @@ class StubGenerator: public StubCodeGenerator { // x29 < MacroAssembler::zero_words_block_size. address generate_zero_blocks() { + StubId stub_id = StubId::stubgen_zero_blocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); Label done; const Register base = x28, cnt = x29, tmp1 = x30, tmp2 = x31; - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_zero_blocks_id; - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (UseBlockZeroing) { int zicboz_block_size = VM_Version::zicboz_block_size.value(); @@ -711,6 +862,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -733,6 +887,12 @@ class StubGenerator: public StubCodeGenerator { // s and d are adjusted to point to the remaining words to copy // address generate_copy_longs(StubId stub_id, Register s, Register d, Register count) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } BasicType type; copy_direction direction; switch (stub_id) { @@ -762,7 +922,7 @@ class StubGenerator: public StubCodeGenerator { Label again, drain; StubCodeMark mark(this, stub_id); __ align(CodeEntryAlignment); - address start = __ pc(); + start = __ pc(); if (direction == copy_forwards) { __ sub(s, s, bias); @@ -879,6 +1039,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1196,18 +1359,43 @@ class StubGenerator: public StubCodeGenerator { break; } + // all stubs provide a 2nd entry which omits the frame push for + // use when bailing out from a conjoint copy. However we may also + // need some extra addressses for memory access protection. + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + assert(nopush_entry != nullptr, "all disjoint copy stubs export a nopush entry"); + + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
    entries; + GrowableArray
    extras; + GrowableArray
    *extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, &entries, extras_ptr); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + *nopush_entry = entries.at(0); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + const Register s = c_rarg0, d = c_rarg1, count = c_rarg2; RegSet saved_reg = RegSet::of(s, d, count); __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); - if (nopush_entry != nullptr) { - *nopush_entry = __ pc(); - // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) - BLOCK_COMMENT("Entry:"); - } + *nopush_entry = __ pc(); + entries.append(*nopush_entry); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + BLOCK_COMMENT("Entry:"); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; if (dest_uninitialized) { @@ -1227,8 +1415,7 @@ class StubGenerator: public StubCodeGenerator { { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, size); } @@ -1244,6 +1431,20 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mv(x10, zr); // return 0 __ ret(); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, &entries, extras_ptr); + return start; } @@ -1272,8 +1473,6 @@ class StubGenerator: public StubCodeGenerator { // used by some other conjoint copy method // address generate_conjoint_copy(StubId stub_id, address nooverlap_target, address *nopush_entry) { - const Register s = c_rarg0, d = c_rarg1, count = c_rarg2; - RegSet saved_regs = RegSet::of(s, d, count); int size; bool aligned; bool is_oop; @@ -1354,12 +1553,45 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); } + // only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + + // We need to protect memory accesses in certain cases + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
    entries; + GrowableArray
    extras; + GrowableArray
    *entries_ptr = (nopush_entry != nullptr ? &entries : nullptr); + GrowableArray
    *extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entries count %d", entries.length()); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + + const Register s = c_rarg0, d = c_rarg1, count = c_rarg2; + RegSet saved_regs = RegSet::of(s, d, count); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1390,8 +1622,7 @@ class StubGenerator: public StubCodeGenerator { { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, -size); } @@ -1405,6 +1636,20 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mv(x10, zr); // return 0 __ ret(); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus any no_push entry and/or + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -1457,6 +1702,27 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); } + // The normal stub provides a 2nd entry which omits the frame push + // for use when bailing out from a disjoint copy. + // Only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + GrowableArray
    entries; + GrowableArray
    *entries_ptr = (expected_entry_count == 1 ? nullptr : &entries); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + address start = load_archive_data(stub_id, entries_ptr); + if (start != nullptr) { + assert(entries.length() + 1 == expected_entry_count, + "expected entry count %d does not match return entry count %d for stub %s", + expected_entry_count, entries.length() + 1, StubInfo::name(stub_id)); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + return start; + } + Label L_load_element, L_store_element, L_do_card_marks, L_done, L_done_pop; // Input registers (after setup_arg_regs) @@ -1489,13 +1755,14 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame // Caller of this entry point must set up the argument registers. if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); BLOCK_COMMENT("Entry:"); } @@ -1598,6 +1865,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end plus any no_push entry + store_archive_data(stub_id, start, __ pc(), entries_ptr); + return start; } @@ -1633,10 +1903,23 @@ class StubGenerator: public StubCodeGenerator { } address generate_unsafecopy_common_error_exit() { - address start = __ pc(); + StubId stub_id = StubId::stubgen_unsafecopy_common_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); __ mv(x10, 0); __ leave(); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1651,10 +1934,22 @@ class StubGenerator: public StubCodeGenerator { // c_rarg2 - byte value // address generate_unsafe_setmemory() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_unsafe_setmemory_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + // we expect one set of extra unsafememory access handler entries + GrowableArray
    extras; + int extra_count = 1 * UnsafeMemoryAccess::COLUMN_COUNT; + address start = load_archive_data(stub_id, nullptr, &extras); + if (start != nullptr) { + assert(extras.length() == extra_count, + "unexpected extra entry count %d", extras.length()); + register_unsafe_access_handlers(extras, 0, 1); + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // bump this on entry, not on exit: // inc_counter_np(SharedRuntime::_unsafe_set_memory_ctr); @@ -1668,6 +1963,7 @@ class StubGenerator: public StubCodeGenerator { const Register tmp_reg = x29; // temp register // Mark remaining code as such which performs Unsafe accesses. + { UnsafeMemoryAccessMark umam(this, true, false); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -1748,6 +2044,17 @@ class StubGenerator: public StubCodeGenerator { __ bind(L_exit); __ leave(); __ ret(); + // have to exit the block and destroy the UnsafeMemoryAccessMark + // in order to retrieve the handler end address + } + + // install saved handler addresses in extras + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + // record the stub entry and end plus the extras + store_archive_data(stub_id, start, end, nullptr, &extras); return start; } @@ -1771,13 +2078,19 @@ class StubGenerator: public StubCodeGenerator { address long_copy_entry) { assert_cond(byte_copy_entry != nullptr && short_copy_entry != nullptr && int_copy_entry != nullptr && long_copy_entry != nullptr); + StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_long_aligned, L_int_aligned, L_short_aligned; const Register s = c_rarg0, d = c_rarg1, count = c_rarg2; __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame // bump this on entry, not on exit: @@ -1804,6 +2117,9 @@ class StubGenerator: public StubCodeGenerator { __ srli(count, count, LogBytesPerLong); // size => long_count __ j(RuntimeAddress(long_copy_entry)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1827,6 +2143,13 @@ class StubGenerator: public StubCodeGenerator { assert_cond(byte_copy_entry != nullptr && short_copy_entry != nullptr && int_copy_entry != nullptr && oop_copy_entry != nullptr && long_copy_entry != nullptr && checkcast_copy_entry != nullptr); + StubId stub_id = StubId::stubgen_generic_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_failed, L_failed_0, L_objArray; Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs; @@ -1842,10 +2165,9 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_generic_arraycopy_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2095,6 +2417,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2140,9 +2465,15 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); @@ -2296,6 +2627,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2476,8 +2810,14 @@ class StubGenerator: public StubCodeGenerator { address generate_aescrypt_encryptBlock() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_aes128, L_aes192; @@ -2493,7 +2833,7 @@ class StubGenerator: public StubCodeGenerator { }; const VectorRegister res = v19; - address start = __ pc(); + start = __ pc(); __ enter(); __ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2532,6 +2872,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2555,8 +2898,14 @@ class StubGenerator: public StubCodeGenerator { address generate_aescrypt_decryptBlock() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_aes128, L_aes192; @@ -2572,7 +2921,7 @@ class StubGenerator: public StubCodeGenerator { }; const VectorRegister res = v19; - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame __ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2611,6 +2960,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2664,8 +3016,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register from = c_rarg0; @@ -2676,7 +3034,7 @@ class StubGenerator: public StubCodeGenerator { const Register keylen = x28; - address start = __ pc(); + start = __ pc(); __ enter(); Label L_aes128, L_aes192; @@ -2698,6 +3056,9 @@ class StubGenerator: public StubCodeGenerator { __ bind(L_aes192); cipherBlockChaining_encryptAESCrypt(13, from, to, key, rvec, input_len); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2753,8 +3114,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_decryptAESCrypt() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register from = c_rarg0; @@ -2765,7 +3132,7 @@ class StubGenerator: public StubCodeGenerator { const Register keylen = x28; - address start = __ pc(); + start = __ pc(); __ enter(); Label L_aes128, L_aes192, L_aes128_loop, L_aes192_loop, L_aes256_loop; @@ -2787,6 +3154,9 @@ class StubGenerator: public StubCodeGenerator { __ bind(L_aes192); cipherBlockChaining_decryptAESCrypt(13, from, to, key, rvec, input_len); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2958,8 +3328,14 @@ class StubGenerator: public StubCodeGenerator { address generate_counterMode_AESCrypt() { assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) and Zbb extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register in = c_rarg0; @@ -2972,7 +3348,7 @@ class StubGenerator: public StubCodeGenerator { const Register keylen = c_rarg7; // temporary register - const address start = __ pc(); + start = __ pc(); __ enter(); Label L_exit; @@ -3002,6 +3378,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3048,11 +3427,17 @@ class StubGenerator: public StubCodeGenerator { address generate_ghash_processBlocks() { assert(UseGHASHIntrinsics, "need GHASH instructions (Zvkg extension) and Zvbb support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Register state = c_rarg0; @@ -3069,6 +3454,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3133,8 +3521,14 @@ class StubGenerator: public StubCodeGenerator { assert(UseGHASHIntrinsics, "need GHASH instructions (Zvkg extension) and Zvbb support"); assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) and Zbb extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register in = c_rarg0; @@ -3158,7 +3552,7 @@ class StubGenerator: public StubCodeGenerator { VectorRegister vtmp2 = v17; VectorRegister vtmp3 = v18; - const address start = __ pc(); + start = __ pc(); __ enter(); Label L_exit; @@ -3193,6 +3587,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3251,6 +3648,12 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3335,17 +3738,27 @@ class StubGenerator: public StubCodeGenerator { __ sub(result, tmp1, tmp2); __ bind(DONE); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_method_entry_barrier() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_method_entry_barrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label deoptimize_label; - address start = __ pc(); + start = __ pc(); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); @@ -3399,6 +3812,9 @@ class StubGenerator: public StubCodeGenerator { __ mv(sp, t0); __ jr(t1); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3423,6 +3839,12 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3519,6 +3941,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(LENGTH_DIFF); __ pop_reg(spilled_regs, sp); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -3555,6 +3981,12 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3778,6 +4210,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(DONE); __ pop_reg(spilled_regs, sp); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -3791,6 +4227,20 @@ class StubGenerator: public StubCodeGenerator { #ifdef COMPILER2 void generate_lookup_secondary_supers_table_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id; + GrowableArray
    entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::_lookup_secondary_supers_table_stubs[0] = start; + for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1); + } + return; + } + StubCodeMark mark(this, stub_id); const Register @@ -3803,7 +4253,13 @@ class StubGenerator: public StubCodeGenerator { r_bitmap = x16; for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { - StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); + address next_entry = __ pc(); + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry; + if (slot == 0) { + start = next_entry; + } else { + entries.append(next_entry); + } Label L_success; __ enter(); __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, result, @@ -3812,14 +4268,22 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); } + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); } // Slow path implementation for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_slow_path_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register r_super_klass = x10, // argument r_array_base = x11, // argument @@ -3832,13 +4296,22 @@ class StubGenerator: public StubCodeGenerator { __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, r_bitmap, result, temp1); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_mulAdd() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_mulAdd_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3856,6 +4329,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -3871,8 +4347,14 @@ class StubGenerator: public StubCodeGenerator { */ address generate_multiplyToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_multiplyToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3897,13 +4379,22 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_squareToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_squareToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3930,6 +4421,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -3943,8 +4437,14 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerLeftShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -3982,6 +4482,9 @@ class StubGenerator: public StubCodeGenerator { __ bind(exit); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -3995,8 +4498,14 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerRightShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -4037,6 +4546,9 @@ class StubGenerator: public StubCodeGenerator { __ bind(exit); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } #endif @@ -4824,9 +5336,19 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_thaw_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_top); + + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4835,11 +5357,20 @@ class StubGenerator: public StubCodeGenerator { // TODO: will probably need multiple return barriers depending on return type StubId stub_id = StubId::stubgen_cont_returnBarrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4847,19 +5378,34 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_returnBarrierExc_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier_exception); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_preempt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ reset_last_Java_frame(true); @@ -4883,6 +5429,9 @@ class StubGenerator: public StubCodeGenerator { __ ld(t1, Address(t1)); __ jr(t1); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5033,53 +5582,6 @@ class StubGenerator: public StubCodeGenerator { // c_rarg3 - int limit // address generate_sha2_implCompress(Assembler::SEW vset_sew, StubId stub_id) { - alignas(64) static const uint32_t round_consts_256[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - }; - alignas(64) static const uint64_t round_consts_512[80] = { - 0x428a2f98d728ae22l, 0x7137449123ef65cdl, 0xb5c0fbcfec4d3b2fl, - 0xe9b5dba58189dbbcl, 0x3956c25bf348b538l, 0x59f111f1b605d019l, - 0x923f82a4af194f9bl, 0xab1c5ed5da6d8118l, 0xd807aa98a3030242l, - 0x12835b0145706fbel, 0x243185be4ee4b28cl, 0x550c7dc3d5ffb4e2l, - 0x72be5d74f27b896fl, 0x80deb1fe3b1696b1l, 0x9bdc06a725c71235l, - 0xc19bf174cf692694l, 0xe49b69c19ef14ad2l, 0xefbe4786384f25e3l, - 0x0fc19dc68b8cd5b5l, 0x240ca1cc77ac9c65l, 0x2de92c6f592b0275l, - 0x4a7484aa6ea6e483l, 0x5cb0a9dcbd41fbd4l, 0x76f988da831153b5l, - 0x983e5152ee66dfabl, 0xa831c66d2db43210l, 0xb00327c898fb213fl, - 0xbf597fc7beef0ee4l, 0xc6e00bf33da88fc2l, 0xd5a79147930aa725l, - 0x06ca6351e003826fl, 0x142929670a0e6e70l, 0x27b70a8546d22ffcl, - 0x2e1b21385c26c926l, 0x4d2c6dfc5ac42aedl, 0x53380d139d95b3dfl, - 0x650a73548baf63del, 0x766a0abb3c77b2a8l, 0x81c2c92e47edaee6l, - 0x92722c851482353bl, 0xa2bfe8a14cf10364l, 0xa81a664bbc423001l, - 0xc24b8b70d0f89791l, 0xc76c51a30654be30l, 0xd192e819d6ef5218l, - 0xd69906245565a910l, 0xf40e35855771202al, 0x106aa07032bbd1b8l, - 0x19a4c116b8d2d0c8l, 0x1e376c085141ab53l, 0x2748774cdf8eeb99l, - 0x34b0bcb5e19b48a8l, 0x391c0cb3c5c95a63l, 0x4ed8aa4ae3418acbl, - 0x5b9cca4f7763e373l, 0x682e6ff3d6b2b8a3l, 0x748f82ee5defb2fcl, - 0x78a5636f43172f60l, 0x84c87814a1f0ab72l, 0x8cc702081a6439ecl, - 0x90befffa23631e28l, 0xa4506cebde82bde9l, 0xbef9a3f7b2c67915l, - 0xc67178f2e372532bl, 0xca273eceea26619cl, 0xd186b8c721c0c207l, - 0xeada7dd6cde0eb1el, 0xf57d4f7fee6ed178l, 0x06f067aa72176fbal, - 0x0a637dc5a2c898a6l, 0x113f9804bef90dael, 0x1b710b35131c471bl, - 0x28db77f523047d84l, 0x32caab7b40c72493l, 0x3c9ebe0a15c9bebcl, - 0x431d67c49c100d4cl, 0x4cc5d4becb3e42b6l, 0x597f299cfc657e2al, - 0x5fcb6fab3ad6faecl, 0x6c44198c4a475817l - }; const int const_add = vset_sew == Assembler::e32 ? 16 : 32; bool multi_block; @@ -5103,9 +5605,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = _cgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(_cgen, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -5129,7 +5637,7 @@ class StubGenerator: public StubCodeGenerator { __ enter(); - address constant_table = vset_sew == Assembler::e32 ? (address)round_consts_256 : (address)round_consts_512; + address constant_table = vset_sew == Assembler::e32 ? (address)_sha256_round_consts : (address)_sha512_round_consts; la(consts, ExternalAddress(constant_table)); // Register use in this function: @@ -5280,6 +5788,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + _cgen->store_archive_data(stub_id, start, __ pc()); + return start; } }; @@ -5452,7 +5963,6 @@ class StubGenerator: public StubCodeGenerator { // x30 t5 buf6 // x31 t6 buf7 address generate_md5_implCompress(StubId stub_id) { - __ align(CodeEntryAlignment); bool multi_block; switch (stub_id) { case StubId::stubgen_md5_implCompress_id: @@ -5464,8 +5974,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // rotation constants const int S11 = 7; @@ -5668,6 +6185,9 @@ class StubGenerator: public StubCodeGenerator { __ pop_reg(saved_regs, sp); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return (address) start; } @@ -5715,14 +6235,19 @@ class StubGenerator: public StubCodeGenerator { * N depends on single vector register length. */ address generate_chacha20Block() { - Label L_Rounds; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); + Label L_Rounds; const int states_len = 16; const int step = 4; const Register state = c_rarg0; @@ -5805,6 +6330,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return (address) start; } @@ -6030,10 +6558,16 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); RegSet saved_regs = RegSet::range(x18, x27); @@ -6156,6 +6690,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return (address) start; } @@ -6224,26 +6761,16 @@ class StubGenerator: public StubCodeGenerator { * c_rarg5 - isURL, Base64 or URL character set */ address generate_base64_encodeBlock() { - alignas(64) static const char toBase64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - alignas(64) static const char toBase64URL[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_encodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Register src = c_rarg0; @@ -6265,9 +6792,9 @@ class StubGenerator: public StubCodeGenerator { __ add(dst, dst, doff); // load the codec base address - __ la(codec, ExternalAddress((address) toBase64)); + __ la(codec, ExternalAddress((address) _encodeBlock_toBase64)); __ beqz(isURL, ProcessData); - __ la(codec, ExternalAddress((address) toBase64URL)); + __ la(codec, ExternalAddress((address) _encodeBlock_toBase64URL)); __ BIND(ProcessData); // vector version @@ -6376,6 +6903,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return (address) start; } @@ -6459,48 +6989,16 @@ class StubGenerator: public StubCodeGenerator { */ address generate_base64_decodeBlock() { - static const uint8_t fromBase64[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - static const uint8_t fromBase64URL[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_decodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Register src = c_rarg0; @@ -6530,9 +7028,9 @@ class StubGenerator: public StubCodeGenerator { __ mv(dstBackup, dst); // load the codec base address - __ la(codec, ExternalAddress((address) fromBase64)); + __ la(codec, ExternalAddress((address) _decodeBlock_fromBase64)); __ beqz(isURL, ProcessData); - __ la(codec, ExternalAddress((address) fromBase64URL)); + __ la(codec, ExternalAddress((address) _decodeBlock_fromBase64URL)); __ BIND(ProcessData); // vector version @@ -6654,6 +7152,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return (address) start; } @@ -6742,10 +7243,16 @@ class StubGenerator: public StubCodeGenerator { * c_rarg0 - int adler result */ address generate_updateBytesAdler32() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_nmax, L_nmax_loop, L_nmax_loop_entry, L_by16, L_by16_loop, L_by16_loop_unroll, L_by1_loop, L_do_mod, L_combine, L_by1; @@ -6911,6 +7418,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // Required for proper stackwalking of RuntimeStub frame __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6920,8 +7430,14 @@ class StubGenerator: public StubCodeGenerator { // f10 = result (float) // t1 = temporary register address generate_float16ToFloat() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_hf2f_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("float16ToFloat:"); @@ -6963,6 +7479,10 @@ class StubGenerator: public StubCodeGenerator { __ fmv_w_x(dst, t1); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -6971,8 +7491,14 @@ class StubGenerator: public StubCodeGenerator { // f11 = temporary float register // t1 = temporary register address generate_floatToFloat16() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_f2hf_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("floatToFloat16:"); @@ -7001,6 +7527,10 @@ class StubGenerator: public StubCodeGenerator { __ float_to_float16_NaN(dst, src, t0, t1); __ ret(); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -7089,10 +7619,16 @@ static const int64_t right_3_bits = right_n_bits(3); // computation. address generate_poly1305_processBlocks() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_poly1305_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label here; @@ -7208,6 +7744,9 @@ static const int64_t right_3_bits = right_n_bits(3); __ leave(); // Required for proper stackwalking __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7215,9 +7754,16 @@ static const int64_t right_3_bits = right_n_bits(3); assert(UseRVV, "sanity"); const int lmul = 2; const int stride = MaxVectorSize / sizeof(jint) * lmul; + StubId stub_id = StubId::stubgen_arrays_hashcode_powers_of_31_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, "StubRoutines", "arrays_hashcode_powers_of_31"); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); for (int i = stride; i >= 0; i--) { jint power_of_31 = 1; for (int j = i; j > 0; j--) { @@ -7226,6 +7772,9 @@ static const int64_t right_3_bits = right_n_bits(3); __ emit_int32(power_of_31); } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7245,11 +7794,17 @@ static const int64_t right_3_bits = right_n_bits(3); address generate_updateBytesCRC32() { assert(UseCRC32Intrinsics, "what are we doing here?"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // input parameters const Register crc = c_rarg0; // crc @@ -7266,14 +7821,23 @@ static const int64_t right_3_bits = right_n_bits(3); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // exception handler for upcall stubs address generate_upcall_stub_exception_handler() { StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Native caller has no idea how to handle exceptions, // so we just crash here. Up to callee to catch exceptions. @@ -7281,6 +7845,9 @@ static const int64_t right_3_bits = right_n_bits(3); __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception)); __ should_not_reach_here(); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7290,8 +7857,14 @@ static const int64_t right_3_bits = right_n_bits(3); address generate_upcall_stub_load_target() { StubId stub_id = StubId::stubgen_upcall_stub_load_target_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ resolve_global_jobject(j_rarg0, t0, t1); // Load target method from receiver @@ -7305,6 +7878,9 @@ static const int64_t right_3_bits = right_n_bits(3); __ ret(); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7397,16 +7973,30 @@ static const int64_t right_3_bits = right_n_bits(3); if (UseMontgomeryMultiplyIntrinsic) { StubId stub_id = StubId::stubgen_montgomeryMultiply_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); - StubRoutines::_montgomeryMultiply = g.generate_multiply(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); + start = g.generate_multiply(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomeryMultiply = start; } if (UseMontgomerySquareIntrinsic) { StubId stub_id = StubId::stubgen_montgomerySquare_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); - StubRoutines::_montgomerySquare = g.generate_square(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); + start = g.generate_square(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomerySquare = start; } if (UseAESIntrinsics) { @@ -7506,8 +8096,27 @@ static const int64_t right_3_bits = right_n_bits(3); break; }; } +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
    & external_addresses) { + // external data defined in this file +#define ADD(addr) external_addresses.append((address)(addr)); + ADD(_encodeBlock_toBase64); + ADD(_encodeBlock_toBase64URL); + ADD(_decodeBlock_fromBase64); + ADD(_decodeBlock_fromBase64URL); + ADD(_sha256_round_consts); + ADD(_sha512_round_consts); +#undef ADD + } +#endif // INCLUDE_CDS }; // end class declaration void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { StubGenerator g(code, blob_id, stub_data); } + +#if INCLUDE_CDS +void StubGenerator_init_AOTAddressTable(GrowableArray
    & addresses) { + StubGenerator::init_AOTAddressTable(addresses); +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index b7f69eff9fa3..bedb67fdda6a 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -507,7 +507,17 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] = }; #if INCLUDE_CDS -// nothing to do for riscv +extern void StubGenerator_init_AOTAddressTable(GrowableArray
    & external_addresses); + void StubRoutines::init_AOTAddressTable() { + ResourceMark rm; + GrowableArray
    external_addresses; + // publish static addresses referred to by riscv generator + // n.b. we have to use an extern call here because class + // StubGenerator, which provides the static method that knows how to + // add the relevant addresses, is declared in a source file rather + // than in a separately includeable header. + StubGenerator_init_AOTAddressTable(external_addresses); + AOTCodeCache::publish_external_addresses(external_addresses); } #endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 22fd66a8da32..35fbf2c1c913 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -30,6 +30,7 @@ #include "runtime/vm_version.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" +#include "utilities/ostream.hpp" #include @@ -509,3 +510,62 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) { } return true; } + +int VM_Version::cpu_features_size() { + return sizeof(RVExtFeatures); +} + +void VM_Version::store_cpu_features(void* buf) { + memcpy(buf, RVExtFeatures::current(), sizeof(RVExtFeatures)); +} + +bool VM_Version::verify_aot_code_cache_features(void* features_buffer) { + RVExtFeatures* features_to_test = (RVExtFeatures*)features_buffer; + return RVExtFeatures::current()->verify_aot_code_cache_features(features_to_test); +} + +// Print one feature using the same spelling as features_string(): single letter +// extensions appear as "rvc"/"rvv" and multi-character extensions with a lower +// case leading character ("Zba" -> "zba"). Must stay in sync with the feature +// string built in VM_Version::setup_cpu_available_features(). +void VM_Version::print_feature_name(stringStream& ss, RVFeatureValue* feature) { + const char* pretty = feature->pretty(); + if (strlen(pretty) == 1) { + ss.print("rv%s", pretty); + } else { + ss.print("%c%s", (char)tolower(pretty[0]), &pretty[1]); + } +} + +void VM_Version::insert_features_names(RVExtFeatures* features, stringStream& ss) { + const char* sep = ""; + int i = 0; + while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) { + if (features->support_feature(i)) { + ss.print("%s", sep); + print_feature_name(ss, _feature_list[i]); + sep = ", "; + } + i += 1; + } +} + +void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) { + RVExtFeatures* features = (RVExtFeatures*)features_buffer; + insert_features_names(features, ss); +} + +void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) { + RVExtFeatures* rv_ext_features_set1 = (RVExtFeatures*)features_set1; + RVExtFeatures* rv_ext_features_set2 = (RVExtFeatures*)features_set2; + const char* sep = ""; + int i = 0; + while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) { + if (rv_ext_features_set1->support_feature(i) && !rv_ext_features_set2->support_feature(i)) { + ss.print("%s", sep); + print_feature_name(ss, _feature_list[i]); + sep = ", "; + } + i += 1; + } +} diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 8e61ff550ece..7a1ec2ba22c3 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -36,6 +36,7 @@ #include "utilities/sizes.hpp" class RiscvHwprobe; +class stringStream; class VM_Version : public Abstract_VM_Version { friend RiscvHwprobe; @@ -396,6 +397,15 @@ class VM_Version : public Abstract_VM_Version { int idx = element_index(f); return (_features_bitmap[idx] & feature_bit(f)) != 0; } + + bool verify_aot_code_cache_features(RVExtFeatures* features_to_test) const { + for (int i = 0; i < element_count(); i++) { + if (_features_bitmap[i] != features_to_test->_features_bitmap[i]) { + return false; + } + } + return true; + } }; // enable extensions based on profile, current supported profiles: @@ -523,6 +533,17 @@ class VM_Version : public Abstract_VM_Version { // Check intrinsic support static bool is_intrinsic_supported(vmIntrinsicID id); + + // AOT Code Cache support + static int cpu_features_size(); + static void store_cpu_features(void* buf); + static bool verify_aot_code_cache_features(void* features_buffer); + static void get_cpu_features_name(void* features_buffer, stringStream& ss); + static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss); + + private: + static void print_feature_name(stringStream& ss, RVFeatureValue* feature); + static void insert_features_names(RVExtFeatures* features, stringStream& ss); }; #endif // CPU_RISCV_VM_VERSION_RISCV_HPP diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp index 58a3c8b231c5..bcbe64788213 100644 --- a/src/hotspot/cpu/s390/frame_s390.cpp +++ b/src/hotspot/cpu/s390/frame_s390.cpp @@ -760,8 +760,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp } bool frame::was_augmented_on_entry(int& real_size) const { - assert(is_compiled_frame(), ""); - if (_cb->as_nmethod_or_null()->needs_stack_repair()) { + assert(_cb != nullptr && _cb->is_nmethod(), ""); + if (_cb->as_nmethod()->needs_stack_repair()) { Unimplemented(); } real_size = _cb->frame_size(); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index b434b3bd9caa..e8971e7630e6 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -4265,15 +4265,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int assert(test_bit <= 0xFFFF, "must fit in low 16 bits for z_tmll"); // Load mark word z_lg(temp_reg, oopDesc::mark_offset_in_bytes(), oop); - if (!UseObjectMonitorTable) { - Label test_mark_word; - // If unlocked bit is set we can directly use the mark word - z_tmll(temp_reg, markWord::unlocked_value); - z_brnaz(test_mark_word); - // Slow path: use klass prototype - load_prototype_header(temp_reg, oop); - bind(test_mark_word); - } z_tmll(temp_reg, test_bit); // Use branch_optimized to handle both near and far branches automatically branch_optimized(jmp_set ? Assembler::bcondNotAllZero : Assembler::bcondAllZero, jmp_label); @@ -6357,11 +6348,9 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register temp1 // instruction emitted as it is part of C1's null check semantics. z_lg(mark, Address(obj, mark_offset)); - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - const Address om_cache_addr = Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))); - z_mvghi(om_cache_addr, 0); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + const Address om_cache_addr = Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))); + z_mvghi(om_cache_addr, 0); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(temp1, obj); @@ -6506,10 +6495,8 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis // Finish fast lock unsuccessfully. MUST branch to with flag == EQ NearLabel slow_path; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - z_mvghi(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + z_mvghi(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp1, obj); @@ -6587,61 +6574,57 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis const ByteSize omc_monitor_offset = OMCache::monitor_offset(); const ByteSize omc_obj_offset = OMCache::obj_offset(); - if (!UseObjectMonitorTable) { - assert(tmp1_monitor == mark, "should be the same here"); - } else { - const Register tmp1_bucket = tmp1; - const Register hash = Z_R0_scratch; - NearLabel monitor_found; + const Register tmp1_bucket = tmp1; + const Register hash = Z_R0_scratch; + NearLabel monitor_found; - // Save the mark, we might need it to extract the hash. - z_lgr(hash, mark); + // Save the mark, we might need it to extract the hash. + z_lgr(hash, mark); - // Look for the monitor in the current thread's object monitor cache (omc). + // Look for the monitor in the current thread's object monitor cache (omc). - z_lg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset)); - z_cg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset)); - z_bre(monitor_found); + z_lg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset)); + z_cg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset)); + z_bre(monitor_found); - // Get the hash code. - z_srlg(hash, hash, markWord::hash_shift); + // Get the hash code. + z_srlg(hash, hash, markWord::hash_shift); - // Get the table and calculate the bucket's address. - load_const_optimized(tmp2, ObjectMonitorTable::current_table_address()); - z_lg(tmp2, Address(tmp2)); - z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset())); - z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset())); - z_sllg(hash, hash, LogBytesPerWord); - z_agr(tmp1_bucket, hash); + // Get the table and calculate the bucket's address. + load_const_optimized(tmp2, ObjectMonitorTable::current_table_address()); + z_lg(tmp2, Address(tmp2)); + z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset())); + z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset())); + z_sllg(hash, hash, LogBytesPerWord); + z_agr(tmp1_bucket, hash); - // Read the monitor from the bucket. - z_lg(tmp1_monitor, Address(tmp1_bucket)); + // Read the monitor from the bucket. + z_lg(tmp1_monitor, Address(tmp1_bucket)); - // Check if the monitor in the bucket is special (empty, tombstone or removed). - z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); - z_brl(slow_path); + // Check if the monitor in the bucket is special (empty, tombstone or removed). + z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + z_brl(slow_path); - // Check if object matches. - z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset())); - BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_peek_weak_handle_in_nmethod(this, tmp2, tmp2, Z_R0_scratch, slow_path); - z_cgr(obj, tmp2); - z_brne(slow_path); + // Check if object matches. + z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_peek_weak_handle_in_nmethod(this, tmp2, tmp2, Z_R0_scratch, slow_path); + z_cgr(obj, tmp2); + z_brne(slow_path); - // Store the monitor in the current thread's object monitor cache (omc). - z_stg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset)); - z_stg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset)); + // Store the monitor in the current thread's object monitor cache (omc). + z_stg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset)); + z_stg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset)); + + bind(monitor_found); - bind(monitor_found); - } NearLabel monitor_locked; // lock the monitor const Register zero = tmp2; - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset() - monitor_tag); - const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag); + const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset()); + const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset()); // Try to CAS owner (no owner => current thread's _monitor_owner_id). // If csg succeeds then CR=EQ, otherwise, register zero is filled @@ -6659,10 +6642,8 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis z_agsi(recursions_address, 1ll); bind(monitor_locked); - if (UseObjectMonitorTable) { - // Cache the monitor for unlock. - z_stg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - } + // Cache the monitor for unlock. + z_stg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); // set the CC now z_cgr(obj, obj); } @@ -6739,11 +6720,7 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg // so that the runtime can fix any potential anonymous owner. z_lg(mark, Address(obj, mark_offset)); z_tmll(mark, markWord::monitor_value); - if (!UseObjectMonitorTable) { - z_brnaz(inflated); - } else { - z_brnaz(push_and_slow_path); - } + z_brnaz(push_and_slow_path); #ifdef ASSERT // Check header not unlocked (0b01). @@ -6802,25 +6779,20 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg const Register tmp1_monitor = tmp1; - if (!UseObjectMonitorTable) { - assert(tmp1_monitor == mark, "should be the same here"); - } else { - // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack. - z_lg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*) - z_cghi(tmp1_monitor, alignof(ObjectMonitor*)); + // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack. + z_lg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*) + z_cghi(tmp1_monitor, alignof(ObjectMonitor*)); - z_brl(slow_path); - } + z_brl(slow_path); // mark contains the tagged ObjectMonitor*. const Register monitor = mark; - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag}; - const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag}; - const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset() - monitor_tag}; - const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag}; + const Address recursions_address{monitor, ObjectMonitor::recursions_offset()}; + const Address succ_address{monitor, ObjectMonitor::succ_offset()}; + const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset()}; + const Address owner_address{monitor, ObjectMonitor::owner_offset()}; NearLabel not_recursive; const Register recursions = tmp2; @@ -6856,9 +6828,6 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg // Save the monitor pointer in the current thread, so we can try to // reacquire the lock in SharedRuntime::monitor_exit_helper(). - if (!UseObjectMonitorTable) { - z_xilf(monitor, markWord::monitor_value); - } z_stg(monitor, Address(Z_thread, JavaThread::unlocked_inflated_monitor_offset())); z_ltgr(obj, obj); // Set flag = NE diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index f745d80f4c52..84f99215f156 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1628,12 +1628,8 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op } else { Register tmp1 = op->tmp1()->as_register(); Register tmp2 = op->tmp2()->as_register(); - if (left == right) { // same operand, so clearly the same klasses, let's save the check - __ jmp (*op->stub()->entry()); // -> do slow check - } else { - __ cmp_klasses_from_objects(left, right, tmp1, tmp2); - __ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check - } + __ cmp_klasses_from_objects(left, right, tmp1, tmp2); + __ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check // fall through to L_oops_not_equal } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index d503edce992a..6e08f438a4af 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -276,10 +276,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, // Finish fast lock unsuccessfully. MUST jump with ZF == 0 Label slow_path; - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(rax_reg, obj, t); @@ -293,7 +291,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, Label push; - const Register top = UseObjectMonitorTable ? rax_reg : box; + const Register top = rax_reg; // Load the mark. movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); @@ -320,10 +318,9 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, lock(); cmpxchgptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); jcc(Assembler::notEqual, slow_path); - if (UseObjectMonitorTable) { - // Need to reload top, clobbered by CAS. - movl(top, Address(thread, JavaThread::lock_stack_top_offset())); - } + // Need to reload top, clobbered by CAS. + movl(top, Address(thread, JavaThread::lock_stack_top_offset())); + bind(push); // After successful lock, push object on lock-stack. movptr(Address(thread, top), obj); @@ -340,63 +337,57 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg, const ByteSize omc_monitor_offset = OMCache::monitor_offset(); const ByteSize omc_obj_offset = OMCache::obj_offset(); - if (!UseObjectMonitorTable) { - assert(mark == monitor, "should be the same here"); - } else { - const Register hash = t; - Label monitor_found; + const Register hash = t; + Label monitor_found; - // Look for the monitor in the current thread's object monitor cache (omc). + // Look for the monitor in the current thread's object monitor cache (omc). - movptr(monitor, Address(thread, thr_omc_offset + omc_monitor_offset)); - cmpptr(obj, Address(thread, thr_omc_offset + omc_obj_offset)); - jccb(Assembler::equal, monitor_found); + movptr(monitor, Address(thread, thr_omc_offset + omc_monitor_offset)); + cmpptr(obj, Address(thread, thr_omc_offset + omc_obj_offset)); + jccb(Assembler::equal, monitor_found); - // Look for the monitor in the table. + // Look for the monitor in the table. - // Get the hash code. - movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes())); - shrq(hash, markWord::hash_shift); - andq(hash, markWord::hash_mask); + // Get the hash code. + movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes())); + shrq(hash, markWord::hash_shift); + andq(hash, markWord::hash_mask); - // Get the table and calculate the bucket's address. - lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address())); - movptr(rax_reg, Address(rax_reg)); - andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset())); - movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset())); + // Get the table and calculate the bucket's address. + lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address())); + movptr(rax_reg, Address(rax_reg)); + andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset())); + movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset())); - // Read the monitor from the bucket. - movptr(monitor, Address(rax_reg, hash, Address::times_ptr)); + // Read the monitor from the bucket. + movptr(monitor, Address(rax_reg, hash, Address::times_ptr)); - // Check if the monitor in the bucket is special (empty, tombstone or removed) - cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); - jcc(Assembler::below, slow_path); + // Check if the monitor in the bucket is special (empty, tombstone or removed) + cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special); + jcc(Assembler::below, slow_path); - // Check if object matches. - movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset())); - BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); - bs_asm->try_peek_weak_handle_in_nmethod(this, rax_reg, rax_reg, slow_path); - cmpptr(rax_reg, obj); - jcc(Assembler::notEqual, slow_path); + // Check if object matches. + movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset())); + BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); + bs_asm->try_peek_weak_handle_in_nmethod(this, rax_reg, rax_reg, slow_path); + cmpptr(rax_reg, obj); + jcc(Assembler::notEqual, slow_path); - // Store the monitor in the current thread's object monitor cache (omc). - movptr(Address(thread, thr_omc_offset + omc_monitor_offset), monitor); - movptr(Address(thread, thr_omc_offset + omc_obj_offset), obj); + // Store the monitor in the current thread's object monitor cache (omc). + movptr(Address(thread, thr_omc_offset + omc_monitor_offset), monitor); + movptr(Address(thread, thr_omc_offset + omc_obj_offset), obj); - bind(monitor_found); - } - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag); - const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag); + bind(monitor_found); + + const Address recursions_address(monitor, ObjectMonitor::recursions_offset()); + const Address owner_address(monitor, ObjectMonitor::owner_offset()); Label monitor_locked; // Lock the monitor. - if (UseObjectMonitorTable) { - // Cache the monitor for unlock before trashing box. On failure to acquire - // the lock, the slow path will reset the entry accordingly (see CacheSetter). - movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor); - } + // Cache the monitor for unlock before trashing box. On failure to acquire + // the lock, the slow path will reset the entry accordingly (see CacheSetter). + movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor); // Try to CAS owner (no owner => current thread's _monitor_owner_id). xorptr(rax_reg, rax_reg); @@ -481,7 +472,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, const Register mark = t; const Register monitor = t; - const Register top = UseObjectMonitorTable ? t : reg_rax; + const Register top = t; const Register box = reg_rax; Label dummy; @@ -499,11 +490,6 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, // Load top. movl(top, Address(thread, JavaThread::lock_stack_top_offset())); - if (!UseObjectMonitorTable) { - // Prefetch mark. - movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); - } - // Check if obj is top of lock-stack. cmpptr(obj, Address(thread, top, Address::times_1, -oopSize)); // Top of lock stack was not obj. Must be monitor. @@ -519,10 +505,8 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, // We elide the monitor check, let the CAS fail instead. - if (UseObjectMonitorTable) { - // Load mark. - movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); - } + // Load mark. + movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); // Try to unlock. Transition lock bits 0b00 => 0b01 movptr(reg_rax, mark); @@ -545,9 +529,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, jcc(Assembler::notEqual, inflated_check_lock_stack); stop("Fast Unlock lock on stack"); bind(check_done); - if (UseObjectMonitorTable) { - movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); - } + movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes())); testptr(mark, markWord::monitor_value); jcc(Assembler::notZero, inflated); stop("Fast Unlock not monitor"); @@ -555,20 +537,16 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, bind(inflated); - if (!UseObjectMonitorTable) { - assert(mark == monitor, "should be the same here"); - } else { - // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack. - movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); - // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*) - cmpptr(monitor, alignof(ObjectMonitor*)); - jcc(Assembler::below, slow_path); - } - const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value)); - const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag}; - const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag}; - const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset() - monitor_tag}; - const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag}; + // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack. + movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes())); + // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*) + cmpptr(monitor, alignof(ObjectMonitor*)); + jcc(Assembler::below, slow_path); + + const Address recursions_address{monitor, ObjectMonitor::recursions_offset()}; + const Address succ_address{monitor, ObjectMonitor::succ_offset()}; + const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset()}; + const Address owner_address{monitor, ObjectMonitor::owner_offset()}; Label recursive; @@ -593,9 +571,6 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t, // Save the monitor pointer in the current thread, so we can try to // reacquire the lock in SharedRuntime::monitor_exit_helper(). - if (!UseObjectMonitorTable) { - andptr(monitor, ~(int32_t)markWord::monitor_value); - } movptr(Address(thread, JavaThread::unlocked_inflated_monitor_offset()), monitor); orl(t, 1); // Fast Unlock ZF = 0 diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index 746fe62b4f05..d7ebd9c1a91a 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -660,8 +660,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp } bool frame::was_augmented_on_entry(int& real_size) const { - assert(is_compiled_frame(), ""); - if (_cb->as_nmethod_or_null()->needs_stack_repair()) { + assert(_cb != nullptr && _cb->is_nmethod(), ""); + if (_cb->as_nmethod()->needs_stack_repair()) { // The stack increment resides just below the saved rbp on the stack // and does not account for the return address and rbp (see MacroAssembler::remove_frame). intptr_t* real_frame_size_addr = unextended_sp() + _cb->frame_size() - sender_sp_offset - 1; diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index cedf086ef36e..d5b39f8b0eb8 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -2444,18 +2444,6 @@ void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) { // load mark word movptr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes())); - if (!UseObjectMonitorTable) { - Label test_mark_word; - // check displaced - testl(temp_reg, markWord::unlocked_value); - jccb(Assembler::notZero, test_mark_word); - // slow path use klass prototype - push(rscratch1); - load_prototype_header(temp_reg, oop, rscratch1); - pop(rscratch1); - - bind(test_mark_word); - } testl(temp_reg, test_bit); jcc((jmp_set) ? Assembler::notZero : Assembler::zero, jmp_label); } @@ -10611,10 +10599,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register reg_r // instruction emitted as it is part of C1's null check semantics. movptr(reg_rax, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseObjectMonitorTable) { - // Clear cache in case fast locking succeeds or we need to take the slow-path. - movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0); - } + // Clear cache in case fast locking succeeds or we need to take the slow-path. + movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0); if (DiagnoseSyncOnValueBasedClasses != 0) { load_klass(tmp, obj, rscratch1); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp index 17fdfa61d366..478283bc77a6 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86.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 @@ -59,12 +59,6 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas __ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - if (!UseObjectMonitorTable) { - // check if monitor - __ testptr(result, markWord::monitor_value); - __ jcc(Assembler::notZero, slowCase); - } - // get hash // Read the header and build a mask to get its hash field. // Depend on hash_mask being at most 32 bits and avoid the use of hash_mask_in_place diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp index de8f52a3c056..74ccc892c8ee 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.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. * Copyright (c) 2025, Intel Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -254,7 +254,7 @@ static auto whole_shuffle(Register scratch, KRegister mergeMask1, KRegister merg // swap the second operand (zetas) since the odd slots contain the same number // as the corresponding even one. This is indicated by input2NeedsShuffle=false) // -// The registers to be multiplied are in input1[] and inputs2[]. The results go +// The registers to be multiplied are in input1[] and input2[]. The results go // into output[]. Two scratch[] register arrays are expected. input1[] can // overlap with either output[] or scratch1[] // - If AVX512, all register arrays are of length 4 @@ -279,7 +279,7 @@ static auto whole_montMul(XMMRegister montQInvModR, XMMRegister dilithium_q, // If so, use output: const XMMRegister* scratch = scratch1 == input1 ? output: scratch1; - // scratch = input1_even * intput2_even + // scratch = input1_even * input2_even for (int i = 0; i < regCnt; i++) { __ vpmuldq(scratch[i], input1[i], input2[i], vector_len); } @@ -308,7 +308,7 @@ static auto whole_montMul(XMMRegister montQInvModR, XMMRegister dilithium_q, } } - // scratch1 = input1_even*intput2_even + // scratch1 = input1_even*input2_even for (int i = 0; i < regCnt; i++) { __ vpmuldq(scratch1[i], input1[i], input2[i], vector_len); } @@ -423,7 +423,7 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, // products will be added to and subtracted from the other half of the // coefficients. In each level we just shuffle the coefficients that need to // be multiplied by the zetas in one set, the rest to another set of vector - // registers, then redistribute the addition/substraction results. + // registers, then redistribute the addition/subtraction results. // For levels 0 and 1 the zetas are not different within the 4 xmm registers // that we would use for them, so we use only one register. @@ -649,7 +649,7 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -898,7 +898,7 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, } __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -993,7 +993,7 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, __ jcc(Assembler::notEqual, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -1002,7 +1002,7 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, return start; } -// Dilithium Motgomery multiply an array by a constant. +// Dilithium Montgomery multiply an array by a constant. // Implements // static int implDilithiumMontMulByConstant(int[] coeffs, int constant) {} // @@ -1089,7 +1089,7 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, __ jcc(Assembler::notEqual, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -1357,7 +1357,7 @@ static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, __ jcc(Assembler::notEqual, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 840f848d3baf..cf2d32b6a603 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -349,11 +349,13 @@ static void store4regs(Register address, int offset, int sourceRegs[], } } -// In all 3 invocations of this function we use the same registers: -// xmm0-xmm7 for the input and the result, -// xmm8-xmm15 as scratch registers and -// xmm16-xmm17 for the constants, -// so we don't pass register arguments. +// This stub helper vectorizes the reduction in implKyberBarrettReduceJava. +// In all invocations of this function we use the same registers: +// Input: xmm0-xmm7 (signed short coefficients) +// xmm16: Barrett multiplier +// xmm17: q +// Scratch: xmm8-xmm15 +// Output: xmm0-xmm7 (reduced coefficients each in [0, q]) static void barrettReduce(MacroAssembler *_masm) { for (int i = 0; i < 8; i++) { __ evpmulhw(xmm(i + 8), k0, xmm(i), xmm16, false, Assembler::AVX_512bit); @@ -490,7 +492,7 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen, store4regs(coeffs, 256, xmm4_7, _masm); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -621,7 +623,7 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, store4regs(coeffs, 256, xmm12_15, _masm); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -771,7 +773,7 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen, __ pop_ppx(r12); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -824,7 +826,7 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, store4regs(result, 256, xmm4_7, _masm); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -886,7 +888,7 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, store4regs(result, 256, xmm4_7, _masm); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -975,7 +977,7 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, __ jcc(Assembler::greater, VBMILoop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -1051,7 +1053,7 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, __ jcc(Assembler::greater, Loop); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end @@ -1096,7 +1098,7 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, store4regs(coeffs, 256, xmm4_7, _masm); __ leave(); // required for proper stackwalking of RuntimeStub frame - __ mov64(rax, 0); // return 0 + __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1 __ ret(0); // record the stub entry and end diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp index edfe89f5af00..6c04ef933040 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp @@ -628,7 +628,7 @@ static address generate_sha3_implCompress_avx512(StubId stub_id, // (2) - a lot of shuffles are inevitable, since there are not enough registers. // To save some shuffles, column1-column3 and column2-4 are placed into // the same 128-bit register. Column 0 is also grouped (by rows). -// This means the SHA3 state fits into 12.5 regisers, leaving 3 registers as +// This means the SHA3 state fits into 12.5 registers, leaving 3 registers as // temporaries. This is mostly sufficient, except for the Theta step, where we // have to buy two slots on the stack static address generate_sha3_implCompress_avx2(StubId stub_id, @@ -693,7 +693,7 @@ static address generate_sha3_implCompress_avx2(StubId stub_id, __ subptr(rsp, reg_size*2); // Registers for memory load - // Notice the careful 'missalignment' of pairs. + // Notice the careful 'misalignment' of pairs. // This helps XOR for all blocksizes XMMRegister a0a1, _a2, a3a4; XMMRegister a5a6, a7a8, _a9; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 393f37ef8e36..217edb03f122 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -24809,12 +24809,11 @@ instruct long_to_mask_evex(kReg dst, rRegL src) %{ ins_pipe( pipe_slow ); %} -instruct mask_opers_evex(kReg dst, kReg src1, kReg src2, kReg kscratch) %{ +instruct mask_opers_evex(kReg dst, kReg src1, kReg src2) %{ match(Set dst (AndVMask src1 src2)); match(Set dst (OrVMask src1 src2)); match(Set dst (XorVMask src1 src2)); - effect(TEMP kscratch); - format %{ "mask_opers_evex $dst, $src1, $src2\t! using $kscratch as TEMP" %} + format %{ "mask_opers_evex $dst, $src1, $src2" %} ins_encode %{ const MachNode* mask1 = static_cast(this->in(this->operand_index($src1))); const MachNode* mask2 = static_cast(this->in(this->operand_index($src2))); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 7c7e61098f50..06645f47eb2b 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -79,6 +79,9 @@ #include "utilities/population_count.hpp" #include "utilities/vmError.hpp" #include "windbghelp.hpp" +#if defined(_M_ARM64) +#include CPU_HEADER(pauth) +#endif #if INCLUDE_JFR #include "jfr/jfrEvents.hpp" #include "jfr/support/jfrNativeLibraryLoadEvent.hpp" @@ -6637,6 +6640,19 @@ bool os::win32::platform_print_native_stack(outputStream* st, const void* contex int count = 0; address lastpc_internal = 0; while (count++ < StackPrintLimit) { +#if defined(_M_ARM64) + // On Windows/ARM64, when the CPU is using authenticated pointers, return + // addresses are signed. Unfortunately, `StackWalk64()` does not strip the + // pointer signature, so we need to do this ourself. Since stripping the + // signature is an idempotent operation, we don't need to guard this call + // based on whether pointer authentication is enabled. + address original = (address)stk.AddrPC.Offset; + address stripped = pauth_strip_pointer(original); + stk.AddrPC.Offset = (DWORD64)(uintptr_t)stripped; + + // We updated the stack frame's PC, so keep the context's PC in sync. + ctx.Pc = stk.AddrPC.Offset; +#endif intptr_t* sp = (intptr_t*)stk.AddrStack.Offset; intptr_t* fp = (intptr_t*)stk.AddrFrame.Offset; // NOT necessarily the same as ctx.Rbp! address pc = (address)stk.AddrPC.Offset; diff --git a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp index 26530cca5ba1..260b6ce32a8c 100644 --- a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp @@ -41,12 +41,25 @@ inline void OrderAccess::storeload() { fence(); } #define READ_MEM_BARRIER __atomic_thread_fence(__ATOMIC_ACQUIRE); #define WRITE_MEM_BARRIER __atomic_thread_fence(__ATOMIC_RELEASE); +// A compiler barrier, forcing the C++ compiler to invalidate all memory assumptions +static inline void compiler_barrier() { + __asm__ volatile ("" : : : "memory"); +} + inline void OrderAccess::acquire() { - READ_MEM_BARRIER; + if (UseZtso) { + compiler_barrier(); + } else { + READ_MEM_BARRIER; + } } inline void OrderAccess::release() { - WRITE_MEM_BARRIER; + if (UseZtso) { + compiler_barrier(); + } else { + WRITE_MEM_BARRIER; + } } inline void OrderAccess::fence() { diff --git a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp index d4a306d35b18..7634ac0fd379 100644 --- a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp @@ -77,6 +77,11 @@ #define REG_LR 1 #define REG_FP 8 +// First argument register (x10), used to pass the stop() message +// to the signal handler. +#ifndef REG_A0 +#define REG_A0 10 +#endif #define REG_BCP 22 NOINLINE address os::current_stack_pointer() { @@ -240,11 +245,8 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = SharedRuntime::handle_unsafe_access(thread, next_pc); } } else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) { - // Pull a pointer to the error message out of the instruction - // stream. - const uint64_t *detail_msg_ptr - = (uint64_t*)(pc + NativeInstruction::instruction_size); - const char *detail_msg = (const char *)*detail_msg_ptr; + // A pointer to the message will have been placed in a0 + const char *detail_msg = (const char *)(uc->uc_mcontext.__gregs[REG_A0]); const char *msg = "stop"; if (TraceTraps) { tty->print_cr("trap: %s: (SIGILL)", msg); diff --git a/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.S b/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.S new file mode 100644 index 000000000000..1ce46f3f2014 --- /dev/null +++ b/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.S @@ -0,0 +1,47 @@ +; +; Copyright (c) 2026, Microsoft and/or its affiliates. All rights reserved. +; 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. +; + + ; Remove the AArch64 pointer signature from the pointer argument + ; + ; Both XPACI and XPACLRI can get the job done, with XPACI being more + ; convenient since it can work with any general purpose register. However, + ; unlike XPACI, XPACLRI can be encoded using hint instructions, thus letting + ; us compile this function using older compilers. + ; + ; Since XPACLRI implicitly uses the link register (x30) and we instead want + ; to remove the signature from the function argument (in x0), we temporarily + ; save x30 before restoring it at the end. + + ALIGN 4 + EXPORT pauth_drop_signature_win_arm64 + AREA pauth_text, CODE + +pauth_drop_signature_win_arm64 + mov x9, x30 + mov x30, x0 + hint #7 + mov x0, x30 + mov x30, x9 + ret + + END diff --git a/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.inline.hpp index dff45632243c..6395172e6879 100644 --- a/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.inline.hpp @@ -28,9 +28,10 @@ // OS specific Support for ROP Protection in VM code. // For more details on PAC see pauth_aarch64.hpp. +extern "C" address pauth_drop_signature_win_arm64(address p); + inline address pauth_strip_pointer(address ptr) { - // No PAC support in windows as of yet. - return ptr; + return pauth_drop_signature_win_arm64(ptr); } inline address pauth_sign_return_address(address ret_addr) { diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 52bb4e224b41..19b4d9ae203c 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -3418,6 +3418,11 @@ void LIRGenerator::substitutability_check(If* x, LIRItem& left, LIRItem& right) void LIRGenerator::substitutability_check_common(Value left_val, Value right_val, LIRItem& left, LIRItem& right, LIR_Opr equal_result, LIR_Opr not_equal_result, LIR_Opr result, CodeEmitInfo* info) { + if (left.result() == right.result()) { + __ move(equal_result, result); + return; + } + LIR_Opr tmp1 = LIR_OprFact::illegalOpr; LIR_Opr tmp2 = LIR_OprFact::illegalOpr; diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 9ef96282aeb7..7f39d35bd8d6 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -36,7 +36,7 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" -DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;) +DEBUG_ONLY(InstanceKlass* _aot_init_test_class = nullptr;) bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass"); @@ -65,10 +65,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { // // Check that no user code is executed during the assembly phase. Otherwise the user // code may introduce undesirable environment dependencies into the heap image. - // If any of these two flags are set, we allow user code to be executed + // If AOTInitTestClass is set, we allow user code to be executed // in the assembly phase. Note that these flags are strictly for the purpose // of testing HotSpot and are not available in product builds. - if (AOTInitTestClass == nullptr && ArchiveHeapTestClass == nullptr) { + if (AOTInitTestClass == nullptr) { if (ik->defined_by_boot_loader()) { // We allow boot classes to be AOT-initialized, except for classes from // -Xbootclasspath (cp index >= 1) be AOT-initialized, as such classes may be @@ -258,7 +258,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { } #ifdef ASSERT - if (ik == _aot_init_class) { + if (ik == _aot_init_test_class) { return true; } #endif @@ -356,12 +356,12 @@ void AOTClassInitializer::init_test_class(TRAPS) { vm_exit_during_initialization("Invalid name for AOTInitTestClass", AOTInitTestClass); } - _aot_init_class = InstanceKlass::cast(k); - _aot_init_class->initialize(CHECK); + _aot_init_test_class = InstanceKlass::cast(k); + _aot_init_test_class->initialize(CHECK); } } bool AOTClassInitializer::has_test_class() { - return _aot_init_class != nullptr; + return _aot_init_test_class != nullptr; } #endif diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index aaff14d2b3a1..48b91960ce77 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -98,6 +98,7 @@ class ClassLocationStream { int current() const { return _current; } bool is_empty() const { return _array.length() == 0; } + int size() const { return _array.length(); } }; class BootCpClassLocationStream : public ClassLocationStream { @@ -435,7 +436,7 @@ bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_class } } - log_info(class, path)("ok"); + log_info(class, path)("ok (file size and timestamp have not changed)"); return true; } @@ -760,7 +761,7 @@ bool AOTClassLocationConfig::check_classpaths(bool is_boot_classpath, bool has_a LogTarget(Info, class, path) lt; if (lt.is_enabled()) { LogStream ls(lt); - ls.print("Checking %s classpath", which); + ls.print("Checking %s classpath from index [%d]", which, index_start); ls.print_cr("%s", use_lcp_match ? " (with longest common prefix substitution)" : ""); ls.print("- expected : '"); print_dumptime_classpath(ls, index_start, index_end, use_lcp_match, _dumptime_lcp_len, runtime_lcp, runtime_lcp_len); @@ -832,68 +833,94 @@ bool AOTClassLocationConfig::check_paths_existence(ClassLocationStream& runtime_ return exist; } -bool AOTClassLocationConfig::check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, - ClassLocationStream& runtime_css, - bool* has_extra_module_paths) const { - if (index_start >= index_end && runtime_css.is_empty()) { // nothing to check - return true; - } - +bool AOTClassLocationConfig::check_module_paths(bool has_aot_linked_classes, bool has_full_module_graph, + ModulePathClassLocationStream& runtime_module_css) const { + const int index_start = module_path_start_index(); + const int index_end = module_path_end_index(); ResourceMark rm; LogTarget(Info, class, path) lt; if (lt.is_enabled()) { LogStream ls(lt); - ls.print_cr("Checking module paths"); + ls.print_cr("Checking module paths from index [%d]", index_start); ls.print("- expected : '"); print_dumptime_classpath(ls, index_start, index_end, false, 0, nullptr, 0); ls.print_cr("'"); ls.print("- actual : '"); - runtime_css.print(&ls); + runtime_module_css.print(&ls); ls.print_cr("'"); } - // Make sure all the dumptime module paths exist and are unchanged + // All JAR files in the dumptime module paths must exist and must be unchanged, + // or else we have a "hard" failure, as we can no longer guarantee that archived + // classes from the module paths remain unchanged at runtime. for (int i = index_start; i < index_end; i++) { const AOTClassLocation* cs = class_location_at(i); const char* dumptime_path = cs->path(); assert(!cs->from_cpattr(), "not applicable for module path"); - log_info(class, path)("Checking '%s' %s", dumptime_path, cs->file_type_string()); + log_info(class, path)("Checking [%d] '%s' %s", i, dumptime_path, cs->file_type_string()); if (!cs->check(dumptime_path, has_aot_linked_classes)) { return false; } } - // We allow runtime_css to be a superset of the module paths specified in dumptime. E.g., - // Dumptime: A:C - // Runtime: A:B:C - runtime_css.start(); + if (!check_module_paths_exact_match(runtime_module_css)) { + if (CDSConfig::new_aot_flags_used()) { + // New AOT workflow requires an exact match for --module-paths. + return false; + } else { + // For classical CDS, we have a "soft" failure if the runtime module paths are + // not an exact match with the dumptime module paths: + // Classes from the module path will not be loaded if they are + // rejected by SystemDictionary::is_shared_class_visible(). + if (has_full_module_graph) { + CDSConfig::disable_full_module_graph(); + AOTMetaspace::report_loading_error("full module graph: disabled because extra module path(s) are specified"); + } + + if (CDSConfig::is_dumping_dynamic_archive() && num_module_paths() > 0) { + CDSConfig::disable_dumping_dynamic_archive(); + aot_log_warning(aot)("Dynamic archiving is disabled because base layer archive has a different module path"); + } + } + } + + return true; +} + +bool AOTClassLocationConfig::check_module_paths_exact_match(ModulePathClassLocationStream& runtime_module_css) const { + if (runtime_module_css.has_non_jar_modules()) { + AOTMetaspace::report_loading_error("module path contains sub-directories or non-JAR files (incompatible with full module graph)"); + return false; + } + + const int index_start = module_path_start_index(); + const int index_end = module_path_end_index(); + + runtime_module_css.start(); for (int i = index_start; i < index_end; i++) { const AOTClassLocation* cs = class_location_at(i); const char* dumptime_path = cs->path(); - while (true) { - if (!runtime_css.has_next()) { - aot_log_warning(aot)("module path has fewer elements than expected"); - *has_extra_module_paths = true; - return true; - } - // Both this->class_locations() and runtime_css are alphabetically sorted. Skip - // items in runtime_css until we see dumptime_path. - const char* runtime_path = runtime_css.get_next(); - if (!os::same_files(dumptime_path, runtime_path)) { - *has_extra_module_paths = true; - return true; - } else { - break; - } + if (!runtime_module_css.has_next()) { + AOTMetaspace::report_loading_error("module path has fewer elements (%d) than expected (%d)", runtime_module_css.size(), num_module_paths()); + return false; + } + const char* runtime_path = runtime_module_css.get_next(); + // Both dumptime and runtime module paths are alphabetically sorted, so we just need to + // compare each element at the same position. + if (!os::same_files(dumptime_path, runtime_path)) { + AOTMetaspace::report_loading_error("module path at [%d] is different: expected %s actual %s", + i, dumptime_path, runtime_path); + return false; } } - if (runtime_css.has_next()) { - *has_extra_module_paths = true; + if (runtime_module_css.has_next()) { + AOTMetaspace::report_loading_error("module path has more elements (%d) than expected (%d)", runtime_module_css.size(), num_module_paths()); + return false; } return true; @@ -967,65 +994,64 @@ bool AOTClassLocationConfig::need_lcp_match_helper(int start, int end, ClassLoca return true; } -bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const { +bool AOTClassLocationConfig::validate_helper(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const { ResourceMark rm; AllClassLocationStreams all_css; log_locations(cache_filename, /*is_write=*/false); - const char* jrt = ClassLoader::get_jrt_entry()->name(); - log_info(class, path)("Checking [0] (modules image)"); - bool success = class_location_at(0)->check(jrt, has_aot_linked_classes); - log_info(class, path)("Modules image %s validation: %s", jrt, success ? "passed" : "failed"); - if (!success) { + if (!check_jrt(has_aot_linked_classes)) { return false; } - if (class_locations()->length() == 1) { - if ((module_path_start_index() >= module_path_end_index()) && Arguments::get_property("jdk.module.path") != nullptr) { - *has_extra_module_paths = true; - } else { - *has_extra_module_paths = false; - } - } else { - bool use_lcp_match = need_lcp_match(all_css); - const char* runtime_lcp; - size_t runtime_lcp_len; - - log_info(class, path)("Longest common prefix substitution in boot/app classpath matching: %s", - use_lcp_match ? "yes" : "no"); - if (use_lcp_match) { - runtime_lcp = find_lcp(all_css.boot_and_app_cp(), runtime_lcp_len); - log_info(class, path)("Longest common prefix: %s (%zu chars)", runtime_lcp, runtime_lcp_len); - } else { - runtime_lcp = nullptr; - runtime_lcp_len = 0; - } - success = check_classpaths(true, has_aot_linked_classes, boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp(), - use_lcp_match, runtime_lcp, runtime_lcp_len); - log_info(class, path)("Archived boot classpath validation: %s", success ? "passed" : "failed"); + if (!check_classpaths(has_aot_linked_classes, all_css)) { + return false; + } + + bool status = check_module_paths(has_aot_linked_classes, has_full_module_graph, all_css.module_path()); + log_info(class, path)("Archived module path validation: %s", status ? "passed" : "failed"); + return status; +} + +bool AOTClassLocationConfig::check_jrt(bool has_aot_linked_classes) const { + const char* jrt = ClassLoader::get_jrt_entry()->name(); + log_info(class, path)("Checking [0] (modules image)"); + bool status = class_location_at(0)->check(jrt, has_aot_linked_classes); + log_info(class, path)("Modules image %s validation: %s", jrt, status ? "passed" : "failed"); + return status; +} + +bool AOTClassLocationConfig::check_classpaths(bool has_aot_linked_classes, AllClassLocationStreams& all_css) const { + const char* runtime_lcp = nullptr; + size_t runtime_lcp_len = 0; - if (success && need_to_check_app_classpath()) { - success = check_classpaths(false, has_aot_linked_classes, app_cp_start_index(), app_cp_end_index(), all_css.app_cp(), + bool use_lcp_match = need_lcp_match(all_css); + log_info(class, path)("Longest common prefix substitution in boot/app classpath matching: %s", + use_lcp_match ? "yes" : "no"); + if (use_lcp_match) { + runtime_lcp = find_lcp(all_css.boot_and_app_cp(), runtime_lcp_len); + log_info(class, path)("Longest common prefix: %s (%zu chars)", runtime_lcp, runtime_lcp_len); + } + + bool status = check_classpaths(true, has_aot_linked_classes, boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp(), use_lcp_match, runtime_lcp, runtime_lcp_len); - log_info(class, path)("Archived app classpath validation: %s", success ? "passed" : "failed"); - } + log_info(class, path)("Archived boot classpath validation: %s", status ? "passed" : "failed"); - if (success) { - success = check_module_paths(has_aot_linked_classes, module_path_start_index(), module_path_end_index(), - all_css.module_path(), has_extra_module_paths); - log_info(class, path)("Archived module path validation: %s%s", success ? "passed" : "failed", - (*has_extra_module_paths) ? " (extra module paths found)" : ""); - } + if (status && need_to_check_app_classpath()) { + status = check_classpaths(false, has_aot_linked_classes, app_cp_start_index(), app_cp_end_index(), all_css.app_cp(), + use_lcp_match, runtime_lcp, runtime_lcp_len); + log_info(class, path)("Archived app classpath validation: %s", status ? "passed" : "failed"); + } - if (runtime_lcp_len > 0) { - os::free((void*)runtime_lcp); - } + if (runtime_lcp_len > 0) { + os::free((void*)runtime_lcp); } - if (success) { - _runtime_instance = this; - } else { + return status; +} + +bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const { + if (!validate_helper(cache_filename, has_aot_linked_classes, has_full_module_graph)) { const char* mismatch_msg = "shared class paths mismatch"; const char* hint_msg = log_is_enabled(Info, class, path) ? "" : " (hint: enable -Xlog:class+path=info to diagnose the failure)"; @@ -1033,7 +1059,7 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l if (CDSConfig::is_dumping_final_static_archive()) { aot_log_error(aot)("class path and/or module path are not compatible with the " "ones specified when the AOTConfiguration file was recorded%s", hint_msg); - vm_exit_during_initialization("Unable to use create AOT cache.", nullptr); + AOTMetaspace::unrecoverable_writing_error("Unable to use create AOT cache."); } else { aot_log_error(aot)("%s%s", mismatch_msg, hint_msg); AOTMetaspace::unrecoverable_loading_error(); @@ -1041,8 +1067,23 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l } else { AOTMetaspace::report_loading_error("%s%s", mismatch_msg, hint_msg); } + return false; } - return success; + + if (CDSConfig::is_dumping_dynamic_archive()) { + // Only support dynamic dumping with the usage of the default CDS archive + // or a simple base archive. + // If the base layer archive contains additional path component besides + // the runtime image and the -cp, dynamic dumping is disabled. + if (num_boot_classpaths() > 0) { + CDSConfig::disable_dumping_dynamic_archive(); + aot_log_warning(aot)( + "Dynamic archiving is disabled because base layer archive has appended boot classpath"); + } + } + + _runtime_instance = this; + return true; } void AOTClassLocationConfig::log_locations(const char* cache_filename, bool is_write) const { diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp index 89a5e6bc9394..771f4951671c 100644 --- a/src/hotspot/share/cds/aotClassLocation.hpp +++ b/src/hotspot/share/cds/aotClassLocation.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 @@ -36,6 +36,7 @@ class AllClassLocationStreams; class ClassLocationStream; class ClassPathZipEntry; +class ModulePathClassLocationStream; class LogStream; // An AOTClassLocation is a location where the application is configured to load Java classes @@ -161,11 +162,14 @@ class AOTClassLocationConfig : public CHeapObj { Group group, bool parse_manifest, bool from_cpattr); void dumptime_init_helper(TRAPS); + bool validate_helper(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const; + bool check_jrt(bool has_aot_linked_classes) const; + bool check_classpaths(bool has_aot_linked_classes, AllClassLocationStreams& all_css) const; bool check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes, int index_start, int index_end, ClassLocationStream& runtime_css, bool use_lcp_match, const char* runtime_lcp, size_t runtime_lcp_len) const; - bool check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, ClassLocationStream& runtime_css, - bool* has_extra_module_paths) const; + bool check_module_paths(bool has_aot_linked_classes, bool has_full_module_graph, ModulePathClassLocationStream& runtime_module_css) const; + bool check_module_paths_exact_match(ModulePathClassLocationStream& runtime_module_css) const; bool file_exists(const char* filename) const; bool check_paths_existence(ClassLocationStream& runtime_css) const; @@ -270,7 +274,7 @@ class AOTClassLocationConfig : public CHeapObj { AOTClassLocationConfig* write_to_archive() const; // Functions used only during runtime - bool validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const; + bool validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const; bool is_valid_classpath_index(int classpath_index, InstanceKlass* ik); diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 572b973827bd..b6dad1e25137 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -85,6 +85,9 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) { initiate_loading(THREAD, "app", h_system_loader, table->boot2()); initiate_loading(THREAD, "app", h_system_loader, table->platform()); preload_classes_in_table(table->app(), "app", h_system_loader, CHECK); + + // Do this after all boot/platform/app classes are loaded, but before bytecode execution. + HeapShared::load_cached_resolved_methods(); } void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* classes, diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 31840f04c38a..ca0fd2f4e9e9 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1466,6 +1466,7 @@ bool AOTMetaspace::in_aot_cache_static_region(void* p) { // - There's an error that indicates that the archive(s) files were corrupt or otherwise damaged. // - When -XX:+RequireSharedSpaces is specified, AND the JVM cannot load the archive(s) due // to version or classpath mismatch. +[[noreturn]] void AOTMetaspace::unrecoverable_loading_error(const char* message) { report_loading_error("%s", message); @@ -1476,6 +1477,7 @@ void AOTMetaspace::unrecoverable_loading_error(const char* message) { } else { vm_exit_during_initialization("Unable to use shared archive. Unrecoverable archive loading error (run with -Xlog:aot,cds for details)", message); } + ShouldNotReachHere(); } void AOTMetaspace::report_loading_error(const char* format, ...) { @@ -1511,15 +1513,17 @@ void AOTMetaspace::report_loading_error(const char* format, ...) { // This function is called when the JVM is unable to write the specified CDS archive due to an // unrecoverable error. +[[noreturn]] void AOTMetaspace::unrecoverable_writing_error(const char* message) { writing_error(message); vm_direct_exit(1); + ShouldNotReachHere(); } // This function is called when the JVM is unable to write the specified CDS archive due to a // an error. The error will be propagated void AOTMetaspace::writing_error(const char* message) { - aot_log_error(aot)("An error has occurred while writing the shared archive file."); + aot_log_error(aot)("An error has occurred while writing the %s.", CDSConfig::type_of_archive_being_written()); if (message != nullptr) { aot_log_error(aot)("%s", message); } diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 163fff73f8f7..9a2151f73083 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -119,9 +119,9 @@ class AOTMetaspace : AllStatic { static bool preimage_static_archive_dumped() NOT_CDS_RETURN_(false); - static void unrecoverable_loading_error(const char* message = "unrecoverable error"); + [[noreturn]] static void unrecoverable_loading_error(const char* message = "unrecoverable error"); static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0); - static void unrecoverable_writing_error(const char* message = nullptr); + [[noreturn]] static void unrecoverable_writing_error(const char* message = nullptr); static void writing_error(const char* message = nullptr); static void make_method_handle_intrinsics_shareable() NOT_CDS_RETURN; diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 3e51712c75e1..c07884a13cc8 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -767,11 +767,20 @@ void CDSConfig::setup_compiler_args() { void CDSConfig::prepare_for_dumping() { assert(CDSConfig::is_dumping_archive(), "sanity"); + if (is_dumping_classic_static_archive() && AOTClassLinking) { + if (FLAG_IS_CMDLINE(AOTClassLinking)) { + log_warning(cds)("AOTClassLinking is not supported for classic CDS archive"); + } + FLAG_SET_ERGO(AOTClassLinking, false); + FLAG_SET_ERGO(AOTInvokeDynamicLinking, false); + } + if (is_dumping_dynamic_archive() && AOTClassLinking) { if (FLAG_IS_CMDLINE(AOTClassLinking)) { log_warning(cds)("AOTClassLinking is not supported for dynamic CDS archive"); } FLAG_SET_ERGO(AOTClassLinking, false); + FLAG_SET_ERGO(AOTInvokeDynamicLinking, false); } if (is_dumping_dynamic_archive() && !is_using_archive()) { @@ -949,7 +958,7 @@ bool CDSConfig::is_preserving_verification_constraints() { } else if (is_dumping_final_static_archive()) { // writing AOT cache return is_dumping_aot_linked_classes(); } else if (is_dumping_classic_static_archive()) { - return is_dumping_aot_linked_classes(); + return false; } else { return false; } @@ -1055,7 +1064,7 @@ void CDSConfig::stop_using_full_module_graph(const char* reason) { } bool CDSConfig::is_dumping_aot_linked_classes() { - if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) { + if (is_dumping_final_static_archive()) { // FMG is required to guarantee that all cached boot/platform/app classes // are visible in the production run, so they can be unconditionally // loaded during VM bootstrap. diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 640cde848b8d..e6320ad0f858 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -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 @@ -65,11 +65,6 @@ "Average number of symbols per bucket in shared table") \ range(2, 246) \ \ - develop(ccstr, ArchiveHeapTestClass, nullptr, \ - "For JVM internal testing only. The static field named " \ - "\"archivedObjects\" of the specified class is stored in the " \ - "CDS archive heap") \ - \ develop(ccstr, AOTInitTestClass, nullptr, \ "For JVM internal testing only. The specified class is stored " \ "in the initialized state in the AOT cache ") \ diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 81c49d6fc8a8..76f133d2f419 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -405,8 +405,8 @@ bool FileMapInfo::validate_class_location() { assert(CDSConfig::is_using_archive(), "runtime only"); AOTClassLocationConfig* config = header()->class_location_config(); - bool has_extra_module_paths = false; - if (!config->validate(full_path(), header()->has_aot_linked_classes(), &has_extra_module_paths)) { + + if (!config->validate(full_path(), header()->has_aot_linked_classes(), header()->has_full_module_graph())) { if (PrintSharedArchiveAndExit) { AOTMetaspace::set_archive_loading_failed(); return true; @@ -415,30 +415,6 @@ bool FileMapInfo::validate_class_location() { } } - if (header()->has_full_module_graph() && has_extra_module_paths) { - CDSConfig::disable_full_module_graph(); - AOTMetaspace::report_loading_error("full module graph: disabled because extra module path(s) are specified"); - } - - if (CDSConfig::is_dumping_dynamic_archive()) { - // Only support dynamic dumping with the usage of the default CDS archive - // or a simple base archive. - // If the base layer archive contains additional path component besides - // the runtime image and the -cp, dynamic dumping is disabled. - if (config->num_boot_classpaths() > 0) { - CDSConfig::disable_dumping_dynamic_archive(); - aot_log_warning(aot)( - "Dynamic archiving is disabled because base layer archive has appended boot classpath"); - } - if (config->num_module_paths() > 0) { - if (has_extra_module_paths) { - CDSConfig::disable_dumping_dynamic_archive(); - aot_log_warning(aot)( - "Dynamic archiving is disabled because base layer archive has a different module path"); - } - } - } - #if INCLUDE_JVMTI if (_classpath_entries_for_jvmti != nullptr) { os::free(_classpath_entries_for_jvmti); @@ -1870,6 +1846,13 @@ bool FileMapInfo::validate_aot_class_linking() { #endif } + if (CDSConfig::is_dumping_final_static_archive() && header()->aot_class_linking_value() && !CDSConfig::is_dumping_aot_linked_classes()) { + ResourceMark rm; + const char* msg = err_msg("AOT class linking was enabled in training run but has been disabled%s", + (CDSConfig::is_dumping_full_module_graph() ? "" : " due to incompatible module options")); + AOTMetaspace::unrecoverable_writing_error(msg); + } + return true; } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 9f7f5a05ace2..2fc23700d828 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -35,7 +35,7 @@ #include "cds/aotStreamedHeapLoader.hpp" #include "cds/aotStreamedHeapWriter.hpp" #include "cds/archiveBuilder.hpp" -#include "cds/archiveUtils.hpp" +#include "cds/archiveUtils.inline.hpp" #include "cds/cds_globals.hpp" #include "cds/cdsConfig.hpp" #include "cds/cdsEnumKlass.hpp" @@ -67,6 +67,7 @@ #include "oops/oopHandle.inline.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/resolvedMethodTable.hpp" #include "runtime/arguments.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/globals_extension.hpp" @@ -105,14 +106,6 @@ size_t HeapShared::_alloc_size[HeapShared::ALLOC_STAT_SLOTS]; size_t HeapShared::_total_obj_count; size_t HeapShared::_total_obj_size; -#ifndef PRODUCT -#define ARCHIVE_TEST_FIELD_NAME "archivedObjects" -static Array* _archived_ArchiveHeapTestClass = nullptr; -static const char* _test_class_name = nullptr; -static Klass* _test_class = nullptr; -static const ArchivedKlassSubGraphInfoRecord* _test_class_record = nullptr; -#endif - #ifdef ASSERT // All classes that have at least one instance in the cached heap. static ArchivableKlassTable* _dumptime_classes_with_cached_oops = nullptr; @@ -142,9 +135,6 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = { {ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD}, {"java/lang/Module$ArchivedData", "archivedData"}, -#ifndef PRODUCT - {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass -#endif {nullptr, nullptr}, }; @@ -153,6 +143,8 @@ ArchivedKlassSubGraphInfoRecord* HeapShared::_run_time_special_subgraph; GrowableArrayCHeap* HeapShared::_pending_roots = nullptr; OopHandle HeapShared::_scratch_basic_type_mirrors[T_VOID+1]; MetaspaceObjToOopHandleTable* HeapShared::_scratch_objects_table = nullptr; +static GrowableArray* _dumptime_resolved_methods = nullptr; +static Array* _runtime_resolved_methods = nullptr; static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], InstanceKlass* ik) { for (int i = 0; fields[i].valid(); i++) { @@ -651,6 +643,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra m = RegeneratedClasses::maybe_get_regenerated_object(m); InstanceKlass* method_holder = m->method_holder(); AOTArtifactFinder::add_cached_class(method_holder); + _dumptime_resolved_methods->append(HeapShared::append_root(obj)); } } } @@ -723,6 +716,7 @@ void HeapShared::remove_scratch_resolved_references(ConstantPool* src) { void HeapShared::init_dumping() { _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); + _dumptime_resolved_methods = new (mtClassShared) GrowableArray(100, mtClassShared); _pending_roots = new GrowableArrayCHeap(500); _pending_roots->append(nullptr); // root index 0 represents a null oop DEBUG_ONLY(_dumptime_classes_with_cached_oops = new (mtClassShared)ArchivableKlassTable()); @@ -1035,6 +1029,10 @@ void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeap delete _pending_roots; _pending_roots = nullptr; + _runtime_resolved_methods = ArchiveUtils::archive_array(_dumptime_resolved_methods); + delete _dumptime_resolved_methods; + _dumptime_resolved_methods = nullptr; + make_archived_object_cache_gc_safe(); } @@ -1204,19 +1202,9 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { } } -#ifndef PRODUCT - if (!ik->module()->is_named() && ik->package() == nullptr && ArchiveHeapTestClass != nullptr) { - // This class is loaded by ArchiveHeapTestClass - return; - } - const char* testcls_msg = ", or a test class in an unnamed package of an unnamed module"; -#else - const char* testcls_msg = ""; -#endif - ResourceMark rm; - log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s%s", - ik->external_name(), lambda_msg, testcls_msg); + log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s", + ik->external_name(), lambda_msg); AOTMetaspace::unrecoverable_writing_error(); } @@ -1326,34 +1314,35 @@ void HeapShared::write_subgraph_info_table() { d_table->iterate(©); writer.dump(&_run_time_subgraph_info_table, "subgraphs"); -#ifndef PRODUCT - if (ArchiveHeapTestClass != nullptr) { - size_t len = strlen(ArchiveHeapTestClass) + 1; - Array* array = ArchiveBuilder::new_ro_array((int)len); - strncpy(array->adr_at(0), ArchiveHeapTestClass, len); - _archived_ArchiveHeapTestClass = array; - } -#endif if (log_is_enabled(Info, aot, heap)) { print_stats(); } } void HeapShared::serialize_tables(SerializeClosure* soc) { - -#ifndef PRODUCT - soc->do_ptr(&_archived_ArchiveHeapTestClass); - if (soc->reading() && _archived_ArchiveHeapTestClass != nullptr) { - _test_class_name = _archived_ArchiveHeapTestClass->adr_at(0); - setup_test_class(_test_class_name); - } -#endif - _run_time_subgraph_info_table.serialize_header(soc); soc->do_ptr(&_run_time_special_subgraph); + soc->do_ptr(&_runtime_resolved_methods); DEBUG_ONLY(soc->do_ptr(&_runtime_classes_with_cached_oops)); } +void HeapShared::load_cached_resolved_methods() { + precond(CDSConfig::is_using_aot_linked_classes()); + if (_runtime_resolved_methods != nullptr) { + JavaThread* current = JavaThread::current(); + HandleMark hm(current); + for (int i = 0; i < _runtime_resolved_methods->length(); i++) { + int root_index = _runtime_resolved_methods->at(i); + Handle mem_name(current, get_root(root_index, /*clear=*/true)); + Method* method = java_lang_invoke_ResolvedMethodName::vmtarget(mem_name()); + InstanceKlass* holder = method->method_holder(); + holder->set_has_resolved_methods(); + oop o = ResolvedMethodTable::add_method(method, mem_name); + precond(o == mem_name()); + } + } +} + static void verify_the_heap(Klass* k, const char* which) { if (VerifyArchivedFields > 0) { ResourceMark rm; @@ -1517,13 +1506,6 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k); const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); -#ifndef PRODUCT - if (_test_class_name != nullptr && k->name()->equals(_test_class_name) && record != nullptr) { - _test_class = k; - _test_class_record = record; - } -#endif - // Initialize from archived data. Currently this is done only // during VM initialization time. No lock is needed. if (record == nullptr) { @@ -2148,18 +2130,6 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name); ResourceMark rm; // for stringStream::as_string() etc. -#ifndef PRODUCT - bool is_test_class = (ArchiveHeapTestClass != nullptr) && (strcmp(info->klass_name, ArchiveHeapTestClass) == 0); - const char* test_class_name = ArchiveHeapTestClass; -#else - bool is_test_class = false; - const char* test_class_name = ""; // avoid C++ printf checks warnings. -#endif - - if (is_test_class) { - log_warning(aot)("Loading ArchiveHeapTestClass %s ...", test_class_name); - } - Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; @@ -2178,35 +2148,15 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], assert(InstanceKlass::cast(ik)->defined_by_boot_loader(), "Only support boot classes"); - if (is_test_class) { - if (ik->module()->is_named()) { - // We don't want ArchiveHeapTestClass to be abused to easily load/initialize arbitrary - // core-lib classes. You need to at least append to the bootclasspath. - stringStream st; - st.print("ArchiveHeapTestClass %s is not in unnamed module", test_class_name); - THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string()); - } - - if (ik->package() != nullptr) { - // This restriction makes HeapShared::is_a_test_class_in_unnamed_module() easy. - stringStream st; - st.print("ArchiveHeapTestClass %s is not in unnamed package", test_class_name); - THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string()); - } - } else { - if (ik->module()->name() != vmSymbols::java_base()) { - // We don't want to deal with cases when a module is unavailable at runtime. - // FUTURE -- load from archived heap only when module graph has not changed - // between dump and runtime. - stringStream st; - st.print("%s is not in java.base module", info->klass_name); - THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string()); - } + if (ik->module()->name() != vmSymbols::java_base()) { + // We don't want to deal with cases when a module is unavailable at runtime. + // FUTURE -- load from archived heap only when module graph has not changed + // between dump and runtime. + stringStream st; + st.print("%s is not in java.base module", info->klass_name); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string()); } - if (is_test_class) { - log_warning(aot)("Initializing ArchiveHeapTestClass %s ...", test_class_name); - } ik->initialize(CHECK); ArchivableStaticFieldFinder finder(ik, field_name); @@ -2230,89 +2180,8 @@ void HeapShared::init_subgraph_entry_fields(TRAPS) { } } -#ifndef PRODUCT -void HeapShared::setup_test_class(const char* test_class_name) { - ArchivableStaticFieldInfo* p = archive_subgraph_entry_fields; - int num_slots = sizeof(archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo); - assert(p[num_slots - 2].klass_name == nullptr, "must have empty slot that's patched below"); - assert(p[num_slots - 1].klass_name == nullptr, "must have empty slot that marks the end of the list"); - - if (test_class_name != nullptr) { - p[num_slots - 2].klass_name = test_class_name; - p[num_slots - 2].field_name = ARCHIVE_TEST_FIELD_NAME; - } -} - -// See if ik is one of the test classes that are pulled in by -XX:ArchiveHeapTestClass -// during runtime. This may be called before the module system is initialized so -// we cannot rely on InstanceKlass::module(), etc. -bool HeapShared::is_a_test_class_in_unnamed_module(Klass* ik) { - if (_test_class != nullptr) { - if (ik == _test_class) { - return true; - } - Array* klasses = _test_class_record->subgraph_object_klasses(); - if (klasses == nullptr) { - return false; - } - - for (int i = 0; i < klasses->length(); i++) { - Klass* k = klasses->at(i); - if (k == ik) { - Symbol* name; - if (k->is_instance_klass()) { - name = InstanceKlass::cast(k)->name(); - } else if (k->is_objArray_klass()) { - Klass* bk = ObjArrayKlass::cast(k)->bottom_klass(); - if (!bk->is_instance_klass()) { - return false; - } - name = bk->name(); - } else { - return false; - } - - // See KlassSubGraphInfo::check_allowed_klass() - we only allow test classes - // to be: - // (A) java.base classes (which must not be in the unnamed module) - // (B) test classes which must be in the unnamed package of the unnamed module. - // So if we see a '/' character in the class name, it must be in (A); - // otherwise it must be in (B). - if (name->index_of_at(0, "/", 1) >= 0) { - return false; // (A) - } - - return true; // (B) - } - } - } - - return false; -} - -void HeapShared::initialize_test_class_from_archive(JavaThread* current) { - Klass* k = _test_class; - if (k != nullptr && is_archived_heap_in_use()) { - JavaThread* THREAD = current; - ExceptionMark em(THREAD); - const ArchivedKlassSubGraphInfoRecord* record = - resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/false, THREAD); - - // The _test_class is in the unnamed module, so it can't call CDS.initializeFromArchive() - // from its method. So we set up its "archivedObjects" field first, before - // calling its . This is not strictly clean, but it's a convenient way to write unit - // test cases (see test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java). - if (record != nullptr) { - init_archived_fields_for(k, record); - } - resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/true, THREAD); - } -} -#endif - void HeapShared::init_for_dumping(TRAPS) { if (CDSConfig::is_dumping_heap()) { - setup_test_class(ArchiveHeapTestClass); init_subgraph_entry_fields(CHECK); } } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 8f7378a4a5ac..ba7ec626d909 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -442,7 +442,6 @@ class HeapShared: AllStatic { // Run-time only static void clear_root(int index); static void get_segment_indexes(int index, int& segment_index, int& internal_index); - static void setup_test_class(const char* test_class_name) PRODUCT_RETURN; #endif // INCLUDE_CDS_JAVA_HEAP public: @@ -468,11 +467,7 @@ class HeapShared: AllStatic { static void init_heap_writer() NOT_CDS_JAVA_HEAP_RETURN; static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN; static void serialize_tables(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; - -#ifndef PRODUCT - static bool is_a_test_class_in_unnamed_module(Klass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false); - static void initialize_test_class_from_archive(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; -#endif + static void load_cached_resolved_methods() NOT_CDS_JAVA_HEAP_RETURN; static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/ci/ciInstance.cpp b/src/hotspot/share/ci/ciInstance.cpp index dd6976caa25b..790d3f47f87e 100644 --- a/src/hotspot/share/ci/ciInstance.cpp +++ b/src/hotspot/share/ci/ciInstance.cpp @@ -235,6 +235,13 @@ ciConstant ciInstance::field_value_by_offset(int field_offset) { return field_value(field); } +intptr_t ciInstance::hash() const { + VM_ENTRY_MARK; + oop obj = get_oop(); + markWord mark = obj->mark(); + if (mark.is_marked()) return markWord::no_hash; + return mark.hash(); +} // ------------------------------------------------------------------ // ciInstance::print_impl // diff --git a/src/hotspot/share/ci/ciInstance.hpp b/src/hotspot/share/ci/ciInstance.hpp index 706349707a2e..335a605afc34 100644 --- a/src/hotspot/share/ci/ciInstance.hpp +++ b/src/hotspot/share/ci/ciInstance.hpp @@ -67,6 +67,8 @@ class ciInstance : public ciObject { // Constant value of a field at the specified offset. ciConstant field_value_by_offset(int field_offset); + intptr_t hash() const; + ciKlass* java_lang_Class_klass(); char* java_lang_String_str(char* buf, size_t buflen); }; diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 596a0a8415af..d6791fab709c 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -698,6 +698,28 @@ bool ciInstanceKlass::has_object_fields() const { ); } +int ciInstanceKlass::number_of_nonoop_entries_in_acmp_map() const { + VM_ENTRY_MARK; + return get_instanceKlass()->acmp_maps_array()->at(0); +} +int ciInstanceKlass::number_of_oop_entries_in_acmp_map() const { + VM_ENTRY_MARK; + const Array* acmp_maps = get_instanceKlass()->acmp_maps_array(); + int number_of_nonoop_entries = acmp_maps->at(0); + return acmp_maps->length() - number_of_nonoop_entries * 2 - 1; +} +AcmpMapSegment ciInstanceKlass::get_nonoop_segment_of_acmp_map(int i) const { + VM_ENTRY_MARK; + const Array* acmp_maps = get_instanceKlass()->acmp_maps_array(); +#ifdef ASSERT + int number_of_nonoop_entries = acmp_maps->at(0); + assert(0 <= i && i < number_of_nonoop_entries, "illegal index, should be in range [0, %d)", number_of_nonoop_entries); +#endif + int offset = acmp_maps->at(2 * i + 1); + int size = acmp_maps->at(2 * i + 2); + return AcmpMapSegment(offset, size); +} + bool ciInstanceKlass::compute_has_trusted_loader() { ASSERT_IN_VM; oop loader_oop = loader(); diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index 0f49fd197819..160c050d4680 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -29,6 +29,7 @@ #include "ci/ciFlags.hpp" #include "ci/ciKlass.hpp" #include "ci/ciSymbol.hpp" +#include "classfile/classFileParser.hpp" #include "oops/instanceKlass.hpp" // ciInstanceKlass @@ -262,6 +263,10 @@ class ciInstanceKlass : public ciKlass { return _nonstatic_fields->at(i); } + int number_of_oop_entries_in_acmp_map() const; + int number_of_nonoop_entries_in_acmp_map() const; + AcmpMapSegment get_nonoop_segment_of_acmp_map(int i) const; + ciInstanceKlass* unique_concrete_subklass(); bool has_finalizable_subclass(); diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 71a09f8d0142..99aef4bb6c76 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -5483,6 +5483,34 @@ void ClassFileParser::set_fast_acmp_members(InlineKlass* vk) const { #endif // VM_LITTLE_ENDIAN } +// See the declarations of _fast_hashcode_offset and _fast_hashcode_shift in InlineKlass::Members +// for details about the fast path logic, and the meaning of these values. +void ClassFileParser::set_fast_hashcode_members(InlineKlass* vk) const { + if (_layout_info->_oop_acmp_map->length() > 0) { // Oops are not allowed in the fast path + return; + } + if (_layout_info->_nonoop_acmp_map->length() >= 2) { // We handle at most one segment... + return; + } + + if (_layout_info->_nonoop_acmp_map->length() == 0) { + vk->set_fast_hashcode_offset(0); + vk->set_fast_hashcode_shift(0); + return; + } + + assert(_layout_info->_nonoop_acmp_map->length() == 1, "trivially"); + + int piece_size = _layout_info->_nonoop_acmp_map->at(0)._size; + if (piece_size != 1 && piece_size != 2 && piece_size != 4 && piece_size != 8) { // ...and it must have a convenient size + return; + } + + int piece_start = _layout_info->_nonoop_acmp_map->at(0)._offset; + vk->set_fast_hashcode_offset(piece_start - (BytesPerLong - piece_size)); + vk->set_fast_hashcode_shift(BitsPerByte * (BytesPerLong - piece_size)); +} + void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loadhook, const ClassInstanceInfo& cl_inst_info, @@ -5722,6 +5750,10 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, set_fast_acmp_members(vk); } + if (UseHashcodeFastPath) { + set_fast_hashcode_members(vk); + } + vk->initialize_calling_convention(CHECK); } diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 29550a25121a..fe5b58dc4be0 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -543,6 +543,7 @@ class ClassFileParser { void create_acmp_maps(InstanceKlass* ik, TRAPS); void set_fast_acmp_members(InlineKlass* vk) const; + void set_fast_hashcode_members(InlineKlass* vk) const; public: ClassFileParser(ClassFileStream* stream, diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index e3e9b0ee830b..0c5b84e8bff4 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -61,7 +61,6 @@ #include "oops/symbol.hpp" #include "oops/typeArrayOop.inline.hpp" #include "prims/jvmtiExport.hpp" -#include "prims/methodHandles.hpp" #include "prims/resolvedMethodTable.hpp" #include "runtime/continuationJavaClasses.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" diff --git a/src/hotspot/share/classfile/javaStackTraceClasses.cpp b/src/hotspot/share/classfile/javaStackTraceClasses.cpp index 00afb19ac3bf..14c24c8f038d 100644 --- a/src/hotspot/share/classfile/javaStackTraceClasses.cpp +++ b/src/hotspot/share/classfile/javaStackTraceClasses.cpp @@ -26,6 +26,7 @@ #include "classfile/javaStackTraceClasses.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/stringTable.hpp" +#include "classfile/symbolTable.hpp" #include "classfile/vmSymbols.hpp" #include "code/debugInfo.hpp" #include "code/pcDesc.hpp" @@ -40,8 +41,6 @@ #include "oops/refArrayOop.inline.hpp" #include "oops/symbol.hpp" #include "oops/typeArrayOop.inline.hpp" -#include "runtime/continuationEntry.inline.hpp" -#include "runtime/continuationJavaClasses.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaCalls.hpp" @@ -49,55 +48,27 @@ #include "runtime/safepointVerifiers.hpp" #include "runtime/vframe.inline.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/ostream.hpp" #include "utilities/preserveException.hpp" -// Internal methods to compose bits of the backtrace in java_lang_Throwable -class Backtrace: AllStatic { - public: - // Helper backtrace functions to store bci|version together. - static int merge_bci_and_version(int bci, int version); - static int merge_mid_and_cpref(int mid, int cpref); - static int bci_at(unsigned int merged); - static int version_at(unsigned int merged); - static int mid_at(unsigned int merged); - static int cpref_at(unsigned int merged); - static int get_line_number(Method* method, int bci); - static Symbol* get_source_file_name(InstanceKlass* holder, int version); -}; - - -inline int Backtrace::merge_bci_and_version(int bci, int version) { - // only store u2 for version, checking for overflow. - if (version > USHRT_MAX || version < 0) version = USHRT_MAX; - assert((u2)bci == bci, "bci should be short"); - return build_int_from_shorts((u2)version, (u2)bci); -} +// Inline helper functions -inline int Backtrace::merge_mid_and_cpref(int mid, int cpref) { - // only store u2 for mid and cpref, checking for overflow. - assert((u2)mid == mid, "mid should be short"); - assert((u2)cpref == cpref, "cpref should be short"); - return build_int_from_shorts((u2)cpref, (u2)mid); +static inline int64_t merge_method_id_bci_and_version(u2 method_id, u2 bci, int version) { + return (int64_t)(((uint64_t)version << 32) | (uint32_t)build_int_from_shorts(method_id, bci)); } -inline int Backtrace::bci_at(unsigned int merged) { - return extract_high_short_from_int(merged); +static inline int version_at(int64_t merged) { + return (int)((uint64_t)merged >> 32); } -inline int Backtrace::version_at(unsigned int merged) { - return extract_low_short_from_int(merged); +static inline int method_id_at(int64_t merged) { + return extract_low_short_from_int((uint32_t)merged); } -inline int Backtrace::mid_at(unsigned int merged) { - return extract_high_short_from_int(merged); +static inline int bci_at(int64_t merged) { + return extract_high_short_from_int((uint32_t)merged); } -inline int Backtrace::cpref_at(unsigned int merged) { - return extract_low_short_from_int(merged); -} - -inline int Backtrace::get_line_number(Method* method, int bci) { +static inline int get_line_number(Method* method, int bci) { int line_number = 0; if (method->is_native()) { // Negative value different from -1 below, enabling Java code in @@ -111,19 +82,6 @@ inline int Backtrace::get_line_number(Method* method, int bci) { return line_number; } -inline Symbol* Backtrace::get_source_file_name(InstanceKlass* holder, int version) { - // RedefineClasses() currently permits redefine operations to - // happen in parallel using a "last one wins" philosophy. That - // spec laxness allows the constant pool entry associated with - // the source_file_name_index for any older constant pool version - // to be unstable so we shouldn't try to use it. - if (holder->constants()->version() != version) { - return nullptr; - } else { - return holder->source_file_name(); - } -} - // java_lang_Throwable int java_lang_Throwable::_backtrace_offset; @@ -216,9 +174,7 @@ void java_lang_Throwable::print(oop throwable, outputStream* st) { } } -// After this many redefines, the stack trace is unreliable. static inline bool version_matches(Method* method, int version) { - assert(version < USHRT_MAX, "version is too big"); return method != nullptr && (method->constants()->version() == version); } @@ -232,79 +188,29 @@ class BacktraceBuilder: public StackObj { private: refArrayHandle _backtrace; refArrayOop _head; - typeArrayOop _methods; - typeArrayOop _bcis; + typeArrayOop _methods_and_bcis; refArrayOop _mirrors; - typeArrayOop _names; // Needed to insulate method name against redefinition. // True if the top frame of the backtrace is omitted because it shall be hidden. bool _has_hidden_top_frame; int _index; NoSafepointVerifier _nsv; - enum { - trace_methods_offset = java_lang_Throwable::trace_methods_offset, - trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, - trace_mirrors_offset = java_lang_Throwable::trace_mirrors_offset, - trace_names_offset = java_lang_Throwable::trace_names_offset, - trace_conts_offset = java_lang_Throwable::trace_conts_offset, - trace_next_offset = java_lang_Throwable::trace_next_offset, - trace_hidden_offset = java_lang_Throwable::trace_hidden_offset, - trace_size = java_lang_Throwable::trace_size, - trace_chunk_size = java_lang_Throwable::trace_chunk_size - }; - // get info out of chunks - static typeArrayOop get_methods(refArrayHandle chunk) { + static typeArrayOop get_methods_and_bcis(refArrayHandle chunk) { typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset)); assert(methods != nullptr, "method array should be initialized in backtrace"); return methods; } - static typeArrayOop get_bcis(refArrayHandle chunk) { - typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); - assert(bcis != nullptr, "bci array should be initialized in backtrace"); - return bcis; - } static refArrayOop get_mirrors(refArrayHandle chunk) { refArrayOop mirrors = refArrayOop(chunk->obj_at(trace_mirrors_offset)); assert(mirrors != nullptr, "mirror array should be initialized in backtrace"); return mirrors; } - static typeArrayOop get_names(refArrayHandle chunk) { - typeArrayOop names = typeArrayOop(chunk->obj_at(trace_names_offset)); - assert(names != nullptr, "names array should be initialized in backtrace"); - return names; - } static bool has_hidden_top_frame(refArrayHandle chunk) { oop hidden = chunk->obj_at(trace_hidden_offset); return hidden != nullptr; } - public: - - // constructor for new backtrace - BacktraceBuilder(TRAPS): _head(nullptr), _methods(nullptr), _bcis(nullptr), _mirrors(nullptr), _names(nullptr), _has_hidden_top_frame(false) { - expand(CHECK); - _backtrace = refArrayHandle(THREAD, _head); - _index = 0; - } - - BacktraceBuilder(Thread* thread, refArrayHandle backtrace) { - _methods = get_methods(backtrace); - _bcis = get_bcis(backtrace); - _mirrors = get_mirrors(backtrace); - _names = get_names(backtrace); - _has_hidden_top_frame = has_hidden_top_frame(backtrace); - assert(_methods->length() == _bcis->length() && - _methods->length() == _mirrors->length() && - _mirrors->length() == _names->length(), - "method and source information arrays should match"); - - // head is the preallocated backtrace - _head = backtrace(); - _backtrace = refArrayHandle(thread, _head); - _index = 0; - } - void expand(TRAPS) { refArrayHandle old_head(THREAD, _head); PauseNoSafepointVerifier pnsv(&_nsv); @@ -312,35 +218,58 @@ class BacktraceBuilder: public StackObj { refArrayOop head = oopFactory::new_objectArray(trace_size, CHECK); refArrayHandle new_head(THREAD, head); - typeArrayOop methods = oopFactory::new_shortArray(trace_chunk_size, CHECK); + typeArrayOop methods = oopFactory::new_longArray(trace_chunk_size, CHECK); typeArrayHandle new_methods(THREAD, methods); - typeArrayOop bcis = oopFactory::new_intArray(trace_chunk_size, CHECK); - typeArrayHandle new_bcis(THREAD, bcis); - refArrayOop mirrors = oopFactory::new_objectArray(trace_chunk_size, CHECK); refArrayHandle new_mirrors(THREAD, mirrors); - typeArrayOop names = oopFactory::new_symbolArray(trace_chunk_size, CHECK); - typeArrayHandle new_names(THREAD, names); - if (!old_head.is_null()) { old_head->obj_at_put(trace_next_offset, new_head()); } new_head->obj_at_put(trace_methods_offset, new_methods()); - new_head->obj_at_put(trace_bcis_offset, new_bcis()); new_head->obj_at_put(trace_mirrors_offset, new_mirrors()); - new_head->obj_at_put(trace_names_offset, new_names()); new_head->obj_at_put(trace_hidden_offset, nullptr); - _head = new_head(); - _methods = new_methods(); - _bcis = new_bcis(); + _head = new_head(); + _methods_and_bcis = new_methods(); _mirrors = new_mirrors(); - _names = new_names(); _index = 0; } + public: + + // Offsets into oop for backtrace() and constants. + enum { + trace_methods_offset = 0, + trace_mirrors_offset = 1, + trace_next_offset = 2, + trace_hidden_offset = 3, + trace_size = 4, + trace_chunk_size = 32 + }; + + // constructor for new backtrace + BacktraceBuilder(TRAPS): _head(nullptr), _methods_and_bcis(nullptr), _mirrors(nullptr), _has_hidden_top_frame(false) { + expand(CHECK); + _backtrace = refArrayHandle(THREAD, _head); + _index = 0; + } + + BacktraceBuilder(Thread* thread, refArrayHandle backtrace) { + _methods_and_bcis = get_methods_and_bcis(backtrace); + _mirrors = get_mirrors(backtrace); + _has_hidden_top_frame = has_hidden_top_frame(backtrace); + assert(_methods_and_bcis->length() == _mirrors->length(), + "method and source information arrays should match"); + + // head is the preallocated backtrace + _head = backtrace(); + _backtrace = refArrayHandle(thread, _head); + _index = 0; + } + + public: refArrayOop backtrace() { return _backtrace(); } @@ -357,13 +286,9 @@ class BacktraceBuilder: public StackObj { method = mhandle(); } - _methods->ushort_at_put(_index, method->orig_method_idnum()); - _bcis->int_at_put(_index, Backtrace::merge_bci_and_version(bci, method->constants()->version())); - - // Note:this doesn't leak symbols because the mirror in the backtrace keeps the - // klass owning the symbols alive so their refcounts aren't decremented. - Symbol* name = method->name(); - _names->symbol_at_put(_index, name); + _methods_and_bcis->long_at_put(_index, + merge_method_id_bci_and_version( + method->orig_method_idnum(), bci, method->constants()->version())); // We need to save the mirrors in the backtrace to keep the class // from being unloaded while we still have this stack trace. @@ -379,10 +304,10 @@ class BacktraceBuilder: public StackObj { // to indicate that this backtrace has a hidden top frame. // But this code is used before TRUE is allocated. // Therefore let's just use an arbitrary legal oop - // available right here. _methods is a short[]. - assert(_methods != nullptr, "we need a legal oop"); + // available right here. _methods_and_bcis is a long[]. + assert(_methods_and_bcis != nullptr, "we need a legal oop"); _has_hidden_top_frame = true; - _head->obj_at_put(trace_hidden_offset, _methods); + _head->obj_at_put(trace_hidden_offset, _methods_and_bcis); } } }; @@ -391,10 +316,9 @@ struct BacktraceElement : public StackObj { int _method_id; int _bci; int _version; - Symbol* _name; Handle _mirror; - BacktraceElement(Handle mirror, int mid, int version, int bci, Symbol* name) : - _method_id(mid), _bci(bci), _version(version), _name(name), _mirror(mirror) {} + BacktraceElement(Handle mirror, int mid, int version, int bci) : + _method_id(mid), _bci(bci), _version(version), _mirror(mirror) {} }; class BacktraceIterator : public StackObj { @@ -402,36 +326,32 @@ class BacktraceIterator : public StackObj { refArrayHandle _result; refArrayHandle _mirrors; typeArrayHandle _methods; - typeArrayHandle _bcis; - typeArrayHandle _names; void init(refArrayHandle result, Thread* thread) { // Get method id, bci, version and mirror from chunk _result = result; if (_result.not_null()) { - _methods = typeArrayHandle(thread, BacktraceBuilder::get_methods(_result)); - _bcis = typeArrayHandle(thread, BacktraceBuilder::get_bcis(_result)); + _methods = typeArrayHandle(thread, BacktraceBuilder::get_methods_and_bcis(_result)); _mirrors = refArrayHandle(thread, BacktraceBuilder::get_mirrors(_result)); - _names = typeArrayHandle(thread, BacktraceBuilder::get_names(_result)); _index = 0; } } public: BacktraceIterator(refArrayHandle result, Thread* thread) { init(result, thread); - assert(_methods.is_null() || _methods->length() == java_lang_Throwable::trace_chunk_size, "lengths don't match"); + assert(_methods.is_null() || _methods->length() == BacktraceBuilder::trace_chunk_size, "lengths don't match"); } BacktraceElement next(Thread* thread) { + int64_t merged_method_data = _methods->long_at(_index); BacktraceElement e (Handle(thread, _mirrors->obj_at(_index)), - _methods->ushort_at(_index), - Backtrace::version_at(_bcis->int_at(_index)), - Backtrace::bci_at(_bcis->int_at(_index)), - _names->symbol_at(_index)); + method_id_at(merged_method_data), + version_at(merged_method_data), + bci_at(merged_method_data)); _index++; - if (_index >= java_lang_Throwable::trace_chunk_size) { - int next_offset = java_lang_Throwable::trace_next_offset; + if (_index >= BacktraceBuilder::trace_chunk_size) { + int next_offset = BacktraceBuilder::trace_next_offset; // Get next chunk refArrayHandle result (thread, refArrayOop(_result->obj_at(next_offset))); init(result, thread); @@ -444,17 +364,28 @@ class BacktraceIterator : public StackObj { } }; +static inline const char* method_id_to_name(InstanceKlass* holder, int method_id) { + // If no method was found with this original idnum, it was deleted. This is rare + // and has been deprecated. + Method* method = holder->method_with_orig_idnum(method_id); + return method == nullptr ? "" : method->name()->as_C_string(); +} + +static inline Symbol* method_id_to_name_symbol(InstanceKlass* holder, int method_id) { + Method* method = holder->method_with_orig_idnum(method_id); + return (method == nullptr) ? SymbolTable::new_symbol("unknown_deleted_by_redefinition") : method->name(); +} // Print stack trace element to the specified output stream. // The output is formatted into a stringStream and written to the outputStream in one step. static void print_stack_element_to_stream(outputStream* st, Handle mirror, int method_id, - int version, int bci, Symbol* name) { + int version, int bci) { ResourceMark rm; stringStream ss; InstanceKlass* holder = java_lang_Class::as_InstanceKlass(mirror()); const char* klass_name = holder->external_name(); - char* method_name = name->as_C_string(); + const char* method_name = method_id_to_name(holder, method_id); ss.print("\tat %s.%s(", klass_name, method_name); // Print module information @@ -470,17 +401,17 @@ static void print_stack_element_to_stream(outputStream* st, Handle mirror, int m } char* source_file_name = nullptr; - Symbol* source = Backtrace::get_source_file_name(holder, version); + Symbol* source = holder->source_file_name(version); if (source != nullptr) { source_file_name = source->as_C_string(); } - // The method can be null if the requested class version is gone + // Now get the exact method from the current or previous version of the InstanceKlass, if it exists. Method* method = holder->method_with_orig_idnum(method_id, version); if (!version_matches(method, version)) { - ss.print("Redefined)"); + ss.print("(Redefined)"); } else { - int line_number = Backtrace::get_line_number(method, bci); + int line_number = get_line_number(method, bci); if (line_number == -2) { ss.print("Native Method)"); } else { @@ -509,7 +440,7 @@ void java_lang_Throwable::print_stack_element(outputStream *st, Method* method, Handle mirror (Thread::current(), method->method_holder()->java_mirror()); int method_id = method->orig_method_idnum(); int version = method->constants()->version(); - print_stack_element_to_stream(st, mirror, method_id, version, bci, method->name()); + print_stack_element_to_stream(st, mirror, method_id, version, bci); } /** @@ -533,7 +464,7 @@ void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st) while (iter.repeat()) { BacktraceElement bte = iter.next(THREAD); - print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci, bte._name); + print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci); } if (THREAD->can_call_java()) { // Call getCause() which doesn't necessarily return the _cause field. @@ -766,7 +697,6 @@ void java_lang_Throwable::allocate_backtrace(Handle throwable, TRAPS) { set_backtrace(throwable(), bt.backtrace()); } - void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle throwable) { // Fill in stack trace into preallocated backtrace (no GC) @@ -795,7 +725,7 @@ void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle t chunk_count++; // Bail-out for deep stacks - if (chunk_count >= trace_chunk_size) break; + if (chunk_count >= BacktraceBuilder::trace_chunk_size) break; } set_depth(throwable(), chunk_count); log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), chunk_count); @@ -832,13 +762,16 @@ void java_lang_Throwable::get_stack_trace_elements(int depth, Handle backtrace, } InstanceKlass* holder = java_lang_Class::as_InstanceKlass(bte._mirror()); + // Get the exact method if it has been redefined and still exists. methodHandle method (THREAD, holder->method_with_orig_idnum(bte._method_id, bte._version)); + // Get the method name from the method_id. + Symbol* method_name = method_id_to_name_symbol(holder, bte._method_id); java_lang_StackTraceElement::fill_in(stack_trace_element, holder, method, bte._version, bte._bci, - bte._name, + method_name, CHECK); } } @@ -909,7 +842,7 @@ bool java_lang_Throwable::get_top_method_and_bci(oop throwable, Method** method, // If the exception happened in a frame that has been hidden, i.e., // omitted from the back trace, we can not compute the message. - oop hidden = backtrace(throwable)->obj_at(trace_hidden_offset); + oop hidden = backtrace(throwable)->obj_at(BacktraceBuilder::trace_hidden_offset); if (hidden != nullptr) { return false; } @@ -1009,7 +942,7 @@ void java_lang_StackTraceElement::decode_file_and_line(Handle java_class, oop& source_file, int& line_number, TRAPS) { // Fill in source file name and line number. - source = Backtrace::get_source_file_name(holder, version); + source = holder->source_file_name(version); source_file = java_lang_Class::source_file(java_class()); if (source != nullptr) { // Class was not redefined. We can trust its cache if set, @@ -1025,7 +958,7 @@ void java_lang_StackTraceElement::decode_file_and_line(Handle java_class, java_lang_Class::set_source_file(java_class(), source_file); } } - line_number = Backtrace::get_line_number(method(), bci); + line_number = get_line_number(method(), bci); } // java_lang_ClassFrameInfo diff --git a/src/hotspot/share/classfile/javaStackTraceClasses.hpp b/src/hotspot/share/classfile/javaStackTraceClasses.hpp index e4466b625d57..e95757c7b0ba 100644 --- a/src/hotspot/share/classfile/javaStackTraceClasses.hpp +++ b/src/hotspot/share/classfile/javaStackTraceClasses.hpp @@ -44,19 +44,6 @@ class java_lang_Throwable: AllStatic { friend class BacktraceIterator; private: - // Trace constants - enum { - trace_methods_offset = 0, - trace_bcis_offset = 1, - trace_mirrors_offset = 2, - trace_names_offset = 3, - trace_conts_offset = 4, - trace_next_offset = 5, - trace_hidden_offset = 6, - trace_size = 7, - trace_chunk_size = 32 - }; - static int _backtrace_offset; static int _detailMessage_offset; static int _stackTrace_offset; @@ -109,9 +96,6 @@ class java_lang_Throwable: AllStatic { static void java_printStackTrace(Handle throwable, TRAPS); // Gets the method and bci of the top frame (TOS). Returns false if this failed. static bool get_top_method_and_bci(oop throwable, Method** method, int* bci); - - // Debugging - friend class JavaClasses; }; // Interface to java.lang.StackTraceElement objects diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index c7fadeaea9ba..c73d15c44d31 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -617,18 +617,7 @@ void ModuleEntryTable::patch_javabase_entries(JavaThread* current, Handle module for (int i = 0; i < list_length; i++) { Klass* k = list->at(i); assert(k->is_klass(), "List should only hold classes"); -#ifndef PRODUCT - if (HeapShared::is_a_test_class_in_unnamed_module(k)) { - // We allow -XX:ArchiveHeapTestClass to archive additional classes - // into the CDS heap, but these must be in the unnamed module. - ModuleEntry* unnamed_module = ClassLoaderData::the_null_class_loader_data()->unnamed_module(); - Handle unnamed_module_handle(current, unnamed_module->module_oop()); - java_lang_Class::fixup_module_field(k, unnamed_module_handle); - } else -#endif - { - java_lang_Class::fixup_module_field(k, module_handle); - } + java_lang_Class::fixup_module_field(k, module_handle); k->class_loader_data()->dec_keep_alive_ref_count(); } diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 6892d694d63a..1e5f127815b6 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -587,29 +587,59 @@ Modules::ArchivedProperty& Modules::archived_prop(size_t i) { void Modules::ArchivedProperty::runtime_check() const { ResourceMark rm; - const char* runtime_value = get_flattened_value(); + const char* old_value = _archived_value; + const char* new_value = get_flattened_value(); aot_log_info(aot)("archived module property %s: %s", _prop, - _archived_value != nullptr ? _archived_value : "(null)"); + old_value != nullptr ? old_value : "(null)"); + + bool mismatch = false; + const char* old_label1; + const char* old_label2; + const char* new_label1; + const char* new_label2; + + if (CDSConfig::is_dumping_final_static_archive()) { + old_label1 = "in AOTConfiguration"; + old_label2 = ", AOTConfiguration ="; + new_label1 = "for current JVM"; + new_label2 = "current ="; + } else if (CDSConfig::new_aot_flags_used()) { + old_label1 = "in AOTCache"; + old_label2 = ", AOTCache ="; + new_label1 = "for current JVM"; + new_label2 = "current ="; + } else { + old_label1 = "during dump time"; + old_label2 = " dump time"; + new_label1 = "during runtime"; + new_label2 = "runtime"; + } - bool disable = false; - if (runtime_value == nullptr) { - if (_archived_value != nullptr) { - AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value); - disable = true; + if (new_value == nullptr) { + if (old_value != nullptr) { + AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified %s but not %s", + _prop, old_value, old_label1, new_label1); + mismatch = true; } } else { - if (_archived_value == nullptr) { - AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value); - disable = true; - } else if (strcmp(runtime_value, _archived_value) != 0) { - AOTMetaspace::report_loading_error("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value); - disable = true; + if (old_value == nullptr) { + AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified %s but not %s", + _prop, new_value, new_label1, old_label1); + mismatch = true; + } else if (strcmp(new_value, old_value) != 0) { + AOTMetaspace::report_loading_error("Mismatched values for property %s: %s %s%s %s", + _prop, new_label2, new_value, old_label2, old_value); + mismatch = true; } } - if (disable) { + if (mismatch) { AOTMetaspace::report_loading_error("Disabling full module graph"); CDSConfig::disable_full_module_graph(); + + if (CDSConfig::is_dumping_final_static_archive()) { + AOTMetaspace::unrecoverable_writing_error("mismatched module options"); + } } } diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 87af1458c182..6141eeb7bd04 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1003,7 +1003,7 @@ bool SystemDictionary::is_shared_class_visible_impl(Symbol* class_name, // has restricted the classes can be loaded at this step to be only: // [1] cs->is_modules_image(): classes in java.base, or, // [2] HeapShared::is_a_test_class_in_unnamed_module(ik): classes in bootstrap/unnamed module - assert(cl->is_modules_image() || HeapShared::is_a_test_class_in_unnamed_module(ik), + assert(cl->is_modules_image(), "only these classes can be loaded before the module system is initialized"); assert(class_loader.is_null(), "sanity"); return true; diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 662eab3311f6..c29f5726236b 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -204,7 +204,7 @@ uint AOTCodeCache::max_aot_code_size() { // At this point all AOT class linking seetings are finilized // and AOT cache is open so we can map AOT code region. void AOTCodeCache::initialize() { -#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64)) +#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64) || defined(RISCV64)) log_info(aot, codecache, init)("AOT Code Cache is not supported on this platform."); disable_caching(); return; @@ -263,7 +263,7 @@ void AOTCodeCache::initialize() { FLAG_SET_DEFAULT(ForceUnreachable, true); } FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false); -#endif // defined(AMD64) || defined(AARCH64) +#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64) } static AOTCodeCache* opened_cache = nullptr; // Use this until we verify the cache @@ -471,7 +471,7 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) { _useUnalignedLoadStores = UseUnalignedLoadStores; #endif -#if defined(AARCH64) && !defined(ZERO) +#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) _avoidUnalignedAccesses = AvoidUnalignedAccesses; #endif @@ -601,14 +601,14 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { } #endif // defined(X86) && !defined(ZERO) -#if defined(AARCH64) && !defined(ZERO) +#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) // switching on AvoidUnalignedAccesses may affect validity of array // copy stubs and nmethods if (!_avoidUnalignedAccesses && AvoidUnalignedAccesses) { log_config_mismatch(_avoidUnalignedAccesses, AvoidUnalignedAccesses, "AvoidUnalignedAccesses"); return false; } -#endif // defined(AARCH64) && !defined(ZERO) +#endif // (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) return true; } @@ -1941,6 +1941,8 @@ void AOTCodeAddressTable::init_extrs() { ADD_EXTERNAL_ADDRESS(SharedRuntime::allocate_inline_types); #if defined(AARCH64) && !defined(ZERO) ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper); +#endif +#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr()); #endif diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 640e73e68270..bd5a872e181c 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -352,11 +352,26 @@ class AOTStubData : public StackObj { #define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) #endif +#if defined(RISCV64) && !defined(ZERO) +#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \ + do_var(intx, BlockZeroingLowLimit) /* zero blocks stub */ \ + do_var(bool, UseBlockZeroing) /* zero blocks stub and nmethods */ \ + do_var(bool, UseConservativeFence) /* fence encoding in stubs and nmethods */ \ + do_var(bool, UseCtxFencei) /* method entry barrier stub */ \ + do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \ + do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \ + do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \ + // END +#else +#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) +#endif + #define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \ AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \ AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \ AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \ AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \ // END #define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name; @@ -377,7 +392,7 @@ class AOTCodeCache : public CHeapObj { bool _useUnalignedLoadStores; #endif -#if defined(AARCH64) && !defined(ZERO) +#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO) bool _avoidUnalignedAccesses; #endif diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 81c03587416a..2b451ebb5b3a 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -79,8 +79,7 @@ bool CompilationPolicy::must_be_compiled(const methodHandle& m, int comp_level) if (m->has_compiled_code()) return false; // already compiled if (!can_be_compiled(m, comp_level)) return false; - return !UseInterpreter || // must compile all methods - (AlwaysCompileLoopMethods && m->has_loops() && CompileBroker::should_compile_new_jobs()); // eagerly compile loop methods + return !UseInterpreter; // must compile all methods } void CompilationPolicy::maybe_compile_early(const methodHandle& m, TRAPS) { diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 099f29f96bf0..7ca7b57b43b7 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -1744,7 +1744,7 @@ void CompileBroker::compiler_thread_loop() { // Never compile a method if breakpoints are present in it if (method()->number_of_breakpoints() == 0) { // Compile the method. - if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) { + if (UseCompiler && CompileBroker::should_compile_new_jobs()) { invoke_compiler_on_method(task); thread->start_idle_timer(); } else { @@ -2152,7 +2152,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { */ void CompileBroker::handle_full_code_cache(CodeBlobType code_blob_type) { UseInterpreter = true; - if (UseCompiler || AlwaysCompileLoopMethods ) { + if (UseCompiler) { if (xtty != nullptr) { stringStream s; // Dump code cache state into a buffer before locking the tty, diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index f1d69c8c0bf4..db2e90699136 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -369,7 +369,6 @@ class CompileBroker: AllStatic { static void disable_compilation_forever() { UseCompiler = false; - AlwaysCompileLoopMethods = false; AtomicAccess::xchg(&_should_compile_new_jobs, jint(shutdown_compilation)); } diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index 1bed0ce06c1c..4e1cdedf3d82 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -59,7 +59,7 @@ class CompileTask : public CHeapObj { Reason_Tiered, // Tiered-policy Reason_Replay, // ciReplay Reason_Whitebox, // Whitebox API - Reason_MustBeCompiled, // Used for -Xcomp or AlwaysCompileLoopMethods (see CompilationPolicy::must_be_compiled()) + Reason_MustBeCompiled, // Used for -Xcomp (see CompilationPolicy::must_be_compiled()) Reason_Count }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index ac5c4d0c8a73..351f412ce752 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,16 +44,6 @@ void ShenandoahArguments::initialize() { vm_exit_during_initialization("Shenandoah GC is not supported on this platform."); #endif - // Shenandoah relies on the object header bits (including the self-forwarded bit - // at markWord::self_fwd_mask_in_place) being preserved across monitor inflation, - // which only holds with UseObjectMonitorTable. - if (!UseObjectMonitorTable) { - if (FLAG_IS_CMDLINE(UseObjectMonitorTable)) { - vm_exit_during_initialization("Shenandoah requires UseObjectMonitorTable"); - } - FLAG_SET_DEFAULT(UseObjectMonitorTable, true); - } - #if 0 // leave this block as stepping stone for future platforms log_warning(gc)("Shenandoah GC is not fully supported on this platform:"); log_warning(gc)(" concurrent modes are not supported, only STW cycles are enabled;"); @@ -208,6 +198,7 @@ void ShenandoahArguments::initialize() { ShenandoahAllocRateSampleWindow)); } +#ifdef _LP64 if (Arguments::is_valhalla_enabled()) { // Flat atomic payloads may contain embedded oops. Current Valhalla code does not handle // it well, missing the GC barriers. As the temporary kludge, disable compressed oops: @@ -216,6 +207,7 @@ void ShenandoahArguments::initialize() { log_warning(gc)("Shenandoah disables compressed oops to avoid breaking with Valhalla"); FLAG_SET_ERGO(UseCompressedOops, false); } +#endif FullGCForwarding::initialize_flags(MaxHeapSize); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index e3619c3f45f3..4ae1f03a08da 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -110,9 +110,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline oop load_reference_barrier(DecoratorSet decorators, oop obj, T* load_addr); - template - inline oop oop_load(DecoratorSet decorators, T* addr); - template inline oop oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value); @@ -147,6 +144,9 @@ class ShenandoahBarrierSet: public BarrierSet { typedef BarrierSet::AccessBarrier Raw; private: + template + static oop oop_load_common(DecoratorSet resolved_decorators, T* addr); + template static void oop_store_common(T* addr, oop value); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 304d1430c4a2..b2f5fbad5cf0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -187,7 +187,7 @@ inline void ShenandoahBarrierSet::satb_barrier(T *field) { if (ShenandoahSATBBarrier && _heap->is_concurrent_mark_in_progress()) { T heap_oop = RawAccess<>::oop_load(field); if (!CompressedOops::is_null(heap_oop)) { - enqueue(CompressedOops::decode(heap_oop)); + enqueue(CompressedOops::decode_not_null(heap_oop)); } } } @@ -233,14 +233,6 @@ inline void ShenandoahBarrierSet::write_ref_field_post(T* field, oop new_value) *byte = CardTable::dirty_card_val(); } -template -inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) { - oop value = RawAccess<>::oop_load(addr); - value = load_reference_barrier(decorators, value, addr); - keep_alive_if_weak(decorators, value); - return value; -} - template inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value) { shenandoah_assert_not_in_cset_except(nullptr, compare_value, (compare_value == nullptr || ShenandoahHeap::heap()->cancelled_gc())); @@ -274,27 +266,35 @@ inline oop ShenandoahBarrierSet::oop_xchg(DecoratorSet decorators, T* addr, oop return RawAccess<>::oop_atomic_xchg(addr, new_value); } +template +template +inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_common(DecoratorSet resolved_decorators, T* addr) { + // This raw access inherits decorators that are needed for proper memory ordering. + oop value = Raw::template oop_load(addr); + ShenandoahBarrierSet* bs = barrier_set(); + value = bs->load_reference_barrier(resolved_decorators, value, addr); + bs->keep_alive_if_weak(resolved_decorators, value); + return value; +} + template template inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_not_in_heap(T* addr) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "must be absent"); - ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); - return bs->oop_load(decorators, addr); + return oop_load_common(decorators, addr); } template template inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap(T* addr) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "must be absent"); - ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); - return bs->oop_load(decorators, addr); + return oop_load_common(decorators, addr); } template inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap_at(oop base, ptrdiff_t offset) { - ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset); - return bs->oop_load(resolved_decorators, AccessInternal::oop_field_addr(base, offset)); + return oop_load_common(resolved_decorators, AccessInternal::oop_field_addr(base, offset)); } template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 18f8f5a4142d..fe7e5211e0a6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -105,7 +105,7 @@ ShenandoahGC::ShenandoahDegenPoint ShenandoahConcurrentGC::degen_point() const { void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap* const heap) { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_init_update_refs_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent init update refs", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare); EventMark em("%s", msg); @@ -117,8 +117,7 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap void ShenandoahConcurrentGC::entry_update_card_table() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - static const char* msg = "Concurrent update cards"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update cards", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table); EventMark em("%s", msg); @@ -295,7 +294,7 @@ void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent complete abbreviated cycle"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent complete abbreviated cycle", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); EventMark em("%s", msg); @@ -376,7 +375,10 @@ void ShenandoahConcurrentGC::vmop_entry_final_verify() { } void ShenandoahConcurrentGC::entry_init_mark() { - const char* msg = init_mark_event_message(); + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); + + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Mark", ""); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_mark); EventMark em("%s", msg); @@ -388,7 +390,11 @@ void ShenandoahConcurrentGC::entry_init_mark() { } void ShenandoahConcurrentGC::entry_final_mark() { - const char* msg = final_mark_event_message(); + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), + "Should not have forwarded objects during final mark, unless old gen concurrent mark is running"); + + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Mark", ""); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_mark); EventMark em("%s", msg); @@ -400,7 +406,7 @@ void ShenandoahConcurrentGC::entry_final_mark() { } void ShenandoahConcurrentGC::entry_init_update_refs() { - static const char* msg = "Pause Init Update Refs"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Update Refs", ""); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_update_refs); EventMark em("%s", msg); @@ -409,7 +415,7 @@ void ShenandoahConcurrentGC::entry_init_update_refs() { } void ShenandoahConcurrentGC::entry_final_update_refs() { - static const char* msg = "Pause Final Update Refs"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Update Refs", ""); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_update_refs); EventMark em("%s", msg); @@ -421,7 +427,7 @@ void ShenandoahConcurrentGC::entry_final_update_refs() { } void ShenandoahConcurrentGC::entry_final_verify() { - const char* msg = verify_final_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Verify Final", ""); ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_verify); EventMark em("%s", msg); @@ -435,7 +441,7 @@ void ShenandoahConcurrentGC::entry_reset() { TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); { - const char* msg = conc_reset_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset); EventMark em("%s", msg); @@ -450,7 +456,8 @@ void ShenandoahConcurrentGC::entry_scan_remembered_set() { if (_generation->is_young()) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = "Concurrent remembered set scanning"; + + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent remembered set scanning", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::init_scan_rset); EventMark em("%s", msg); @@ -466,7 +473,7 @@ void ShenandoahConcurrentGC::entry_scan_remembered_set() { void ShenandoahConcurrentGC::entry_mark_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = "Concurrent marking roots"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark_roots); EventMark em("%s", msg); @@ -480,8 +487,11 @@ void ShenandoahConcurrentGC::entry_mark_roots() { void ShenandoahConcurrentGC::entry_mark() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); + assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), + "Should not have forwarded objects concurrent mark, unless old gen concurrent mark is running"); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_mark_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark); EventMark em("%s", msg); @@ -496,7 +506,7 @@ void ShenandoahConcurrentGC::entry_mark() { void ShenandoahConcurrentGC::entry_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - static const char* msg = "Concurrent thread roots"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent thread roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_thread_roots); EventMark em("%s", msg); @@ -511,7 +521,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() { void ShenandoahConcurrentGC::entry_weak_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - const char* msg = conc_weak_refs_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak references", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs); EventMark em("%s", msg); @@ -527,7 +537,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() { void ShenandoahConcurrentGC::entry_weak_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_weak_roots_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots); EventMark em("%s", msg); @@ -543,7 +553,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() { void ShenandoahConcurrentGC::entry_class_unloading() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent class unloading"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent class unloading", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_class_unload); EventMark em("%s", msg); @@ -559,7 +569,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() { void ShenandoahConcurrentGC::entry_strong_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent strong roots"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent strong roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_strong_roots); EventMark em("%s", msg); @@ -577,7 +587,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() { void ShenandoahConcurrentGC::entry_cleanup_early() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_cleanup_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_early, true /* log_heap_usage */); EventMark em("%s", msg); @@ -596,8 +606,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() { void ShenandoahConcurrentGC::entry_evacuate() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - static const char* msg = "Concurrent evacuation"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent evacuation", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_evac); EventMark em("%s", msg); @@ -613,8 +622,7 @@ void ShenandoahConcurrentGC::entry_evacuate() { void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - static const char* msg = "Concurrent update thread roots"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update thread roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_thread_roots); EventMark em("%s", msg); @@ -627,7 +635,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() { void ShenandoahConcurrentGC::entry_update_refs() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - static const char* msg = "Concurrent update references"; + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update references", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs); EventMark em("%s", msg); @@ -643,7 +651,7 @@ void ShenandoahConcurrentGC::entry_update_refs() { void ShenandoahConcurrentGC::entry_cleanup_complete() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_cleanup_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_complete, true /* log_heap_usage */); EventMark em("%s", msg); @@ -655,7 +663,7 @@ void ShenandoahConcurrentGC::entry_cleanup_complete() { void ShenandoahConcurrentGC::entry_reset_after_collect() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_reset_after_collect_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset after collect", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset_after_collect); EventMark em("%s", msg); @@ -1250,7 +1258,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() { void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - const char* msg = conc_final_roots_event_message(); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent final roots", ""); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); EventMark em("%s", msg); @@ -1295,100 +1303,3 @@ bool ShenandoahConcurrentGC::check_cancellation_and_abort(ShenandoahDegenPoint p } return false; } - -const char* ShenandoahConcurrentGC::init_mark_event_message() const { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here"); - if (heap->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", ""); - } -} - -const char* ShenandoahConcurrentGC::final_mark_event_message() const { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), - "Should not have forwarded objects during final mark, unless old gen concurrent mark is running"); - - if (heap->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_mark_event_message() const { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(), - "Should not have forwarded objects concurrent mark, unless old gen concurrent mark is running"); - if (heap->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_reset_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_reset_after_collect_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset after collect", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset after collect", ""); - } -} - -const char* ShenandoahConcurrentGC::verify_final_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_final_roots_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Final Roots", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Final Roots", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_weak_refs_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_weak_roots_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_cleanup_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", ""); - } -} - -const char* ShenandoahConcurrentGC::conc_init_update_refs_event_message() const { - if (ShenandoahHeap::heap()->unload_classes()) { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Init Update Refs", " (unload classes)"); - } else { - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Init Update Refs", ""); - } -} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index e763d1853e3f..cf1398e3de69 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -132,20 +132,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void start_mark(); static bool has_in_place_promotions(ShenandoahHeap* heap); - - // Messages for GC trace events, they have to be immortal for - // passing around the logging/tracing systems - const char* init_mark_event_message() const; - const char* final_mark_event_message() const; - const char* verify_final_event_message() const; - const char* conc_final_roots_event_message() const; - const char* conc_mark_event_message() const; - const char* conc_reset_event_message() const; - const char* conc_reset_after_collect_event_message() const; - const char* conc_weak_refs_event_message() const; - const char* conc_weak_roots_event_message() const; - const char* conc_cleanup_event_message() const; - const char* conc_init_update_refs_event_message() const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONCURRENTGC_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 3c3cdc4a90a8..439e23af2812 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -465,21 +465,35 @@ void ShenandoahDegenGC::op_degenerated_futile() { const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) const { switch (point) { - case _degenerated_unset: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " ()"); - case _degenerated_outside_cycle: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Outside of Cycle)"); - case _degenerated_roots: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Roots)"); - case _degenerated_mark: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Mark)"); - case _degenerated_evac: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Evacuation)"); - case _degenerated_update_refs: - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Update Refs)"); - default: + case _degenerated_unset: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " ()"); + return msg; + } + case _degenerated_outside_cycle: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Outside of Cycle)"); + return msg; + } + case _degenerated_roots: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Roots)"); + return msg; + } + case _degenerated_mark: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Mark)"); + return msg; + } + case _degenerated_evac: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Evacuation)"); + return msg; + } + case _degenerated_update_refs: { + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Update Refs)"); + return msg; + } + default: { ShouldNotReachHere(); - SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (?)"); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (?)"); + return msg; + } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 026ba8681e6b..de660badd9ac 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1465,10 +1465,8 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const { st->print_cr("Heap Regions:"); st->print_cr("Region state: EU=empty-uncommitted, EC=empty-committed, R=regular, H=humongous start, HP=pinned humongous start"); st->print_cr(" HC=humongous continuation, CS=collection set, TR=trash, P=pinned, CSP=pinned collection set"); - st->print_cr("BTE=bottom/top/end, TAMS=top-at-mark-start"); - st->print_cr("UWM=update watermark, U=used"); - st->print_cr("T=TLAB allocs, G=GCLAB allocs"); - st->print_cr("S=shared allocs, L=live data"); + st->print_cr("A=age, BTE=bottom/top/end, TAMS=top-at-mark-start, UWM=update watermark, U=used"); + st->print_cr("T=TLAB allocs, G=GCLAB allocs, S=shared allocs, L=live data"); st->print_cr("CP=critical pins"); for (size_t i = 0; i < num_regions(); i++) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index b3c847cadaf6..ff0271db8fda 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2015, 2020, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -139,7 +139,7 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) { // Either we succeed in updating the reference, or something else gets in our way. // We don't care if that is another concurrent GC update, or another mutator update. - atomic_update_oop(fwd, p, obj); + atomic_update_oop(fwd, p, o); } } } @@ -196,14 +196,14 @@ inline void ShenandoahHeap::atomic_update_oop(oop update, oop* addr, oop compare inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, narrowOop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - narrowOop u = CompressedOops::encode(update); + narrowOop u = CompressedOops::encode_not_null(update); AtomicAccess::cmpxchg(addr, compare, u, memory_order_release); } inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, oop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); narrowOop c = CompressedOops::encode(compare); - narrowOop u = CompressedOops::encode(update); + narrowOop u = CompressedOops::encode_not_null(update); AtomicAccess::cmpxchg(addr, c, u, memory_order_release); } @@ -214,14 +214,14 @@ inline bool ShenandoahHeap::atomic_update_oop_check(oop update, oop* addr, oop c inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, narrowOop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - narrowOop u = CompressedOops::encode(update); + narrowOop u = CompressedOops::encode_not_null(update); return (narrowOop) AtomicAccess::cmpxchg(addr, compare, u, memory_order_release) == compare; } inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, oop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); narrowOop c = CompressedOops::encode(compare); - narrowOop u = CompressedOops::encode(update); + narrowOop u = CompressedOops::encode_not_null(update); return CompressedOops::decode(AtomicAccess::cmpxchg(addr, c, u, memory_order_release)) == compare; } @@ -236,7 +236,7 @@ inline void ShenandoahHeap::atomic_clear_oop(oop* addr, oop compare) { inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, oop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - narrowOop cmp = CompressedOops::encode(compare); + narrowOop cmp = CompressedOops::encode_not_null(compare); AtomicAccess::cmpxchg(addr, cmp, narrowOop(), memory_order_relaxed); } @@ -330,15 +330,6 @@ void ShenandoahHeap::increase_object_age(oop obj, uint additional_age) { uint ShenandoahHeap::get_object_age(oop obj) { markWord w = obj->mark(); assert(!w.is_marked(), "must not be forwarded"); - if (UseObjectMonitorTable) { - assert(w.age() <= markWord::max_age, "Impossible!"); - return w.age(); - } - if (w.has_monitor()) { - w = w.monitor()->header(); - } else { - assert(!w.has_displaced_mark_helper(), "Mark word should not be displaced"); - } assert(w.age() <= markWord::max_age, "Impossible!"); return w.age(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index a37fd34238e0..84582ce07c30 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -424,6 +424,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const { } st->print("|%s", shenandoah_affiliation_code(affiliation())); + st->print("|A %2d", age()); #define SHR_PTR_FORMAT "%12" PRIxPTR diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 77c1bab4c3ea..00910d3035ed 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -63,7 +63,7 @@ JRT_LEAF(narrowOop, ShenandoahRuntime::load_reference_barrier_strong_narrow_narr assert(!CompressedOops::is_null(src), "Filtered by caller"); oop s = CompressedOops::decode_not_null(src); oop r = ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(s, load_addr); - return CompressedOops::encode(r); + return CompressedOops::encode_not_null(r); JRT_END JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc* src, oop* load_addr)) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index 4750a0cb2db6..e9761ebeb863 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -47,19 +47,24 @@ class GCTimer; class ShenandoahGeneration; -#define SHENANDOAH_RETURN_EVENT_MESSAGE(generation_type, prefix, postfix) \ +#define SHENANDOAH_EVENT_MESSAGE(loc, generation_type, prefix, postfix) \ + const char* loc; \ switch (generation_type) { \ case NON_GEN: \ - return prefix postfix; \ + loc = prefix postfix; \ + break; \ case GLOBAL: \ - return prefix " (Global)" postfix; \ + loc = prefix " (Global)" postfix; \ + break; \ case YOUNG: \ - return prefix " (Young)" postfix; \ + loc = prefix " (Young)" postfix; \ + break; \ case OLD: \ - return prefix " (Old)" postfix; \ + loc = prefix " (Old)" postfix; \ + break; \ default: \ ShouldNotReachHere(); \ - return prefix " (Unknown)" postfix; \ + loc = prefix " (Unknown)" postfix; \ } \ class ShenandoahGCSession : public StackObj { diff --git a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp index 926079dab0ff..b68bf8ce35e7 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp @@ -32,6 +32,7 @@ #include "gc/z/zHeap.hpp" #include "gc/z/zNMethod.hpp" #include "gc/z/zUtils.inline.hpp" +#include "oops/accessBackend.hpp" #include "oops/inlineKlass.inline.hpp" #include "oops/objArrayOop.hpp" #include "utilities/copy.hpp" @@ -448,125 +449,114 @@ inline void ZBarrierSet::AccessBarrier::clone_in_heap(o clone_obj(to_zaddress(src), to_zaddress(dst), ZUtils::words_to_bytes(size)); } -static inline void copy_primitive_payload(const void* src, const void* dst, const size_t payload_size_bytes, size_t& copied_bytes) { - if (payload_size_bytes == 0) { +// Iterate over a value payload and visit all blocks of primitive fields and all oops. +template +void value_primitive_and_oop_iterate(InlineKlass* klass, + address payload, + size_t payload_size, + PrimitiveFunction primitive_function, + OopFunction oop_function) { + if (!klass->contains_oops()) { + // Only primitive fields + primitive_function(0, payload_size); return; } - void* src_payload = (void*)(address(src) + copied_bytes); - void* dst_payload = (void*)(address(dst) + copied_bytes); - Copy::copy_value_content(src_payload, dst_payload, payload_size_bytes); - copied_bytes += payload_size_bytes; -} + size_t visited = 0; -static inline void clear_primitive_payload(const void* dst, const size_t payload_size_bytes, size_t& copied_bytes) { - if (payload_size_bytes == 0) { - return; - } + // Visit primitive and oop fields up until and including the last oop field + klass->oop_iterate_value_payload_f(payload, + [&](oop* p) { + const size_t oop_offset = (address)p - payload; + + assert(visited <= oop_offset, "Negative sized leading payload segment"); + + // Visit any previous unvisited primitive fields + if (oop_offset > visited) { + const size_t size = oop_offset - visited; + primitive_function(visited, size); + visited += size; + } + + // Visit the oop field + oop_function(oop_offset); + visited += sizeof(zpointer); + }); - void* dst_payload = (void*)(address(dst) + copied_bytes); - Copy::fill_to_memory_atomic(dst_payload, payload_size_bytes); - copied_bytes += payload_size_bytes; + // Visit any trailing primitive payload after the last oop + assert(visited <= payload_size, "Negative sized trailing payload segment"); + if (payload_size > visited) { + const size_t size = payload_size - visited; + primitive_function(visited, size); + } } template inline void ZBarrierSet::AccessBarrier::value_copy_in_heap(const ValuePayload& src, const ValuePayload& dst) { precond(src.klass() == dst.klass()); - const LayoutKind lk = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind()); - const InlineKlass* md = src.klass(); - if (md->contains_oops()) { - assert(!LayoutKindHelper::is_atomic_flat(lk) || - (md->nonstatic_oop_map_count() == 1 && - md->layout_size_in_bytes(lk) == sizeof(zpointer)), - "ZGC can only handle atomic flat values with a single oop"); - - // Iterate over each oop map, performing: - // 1) possibly raw copy for any primitive payload before each map - // 2) load and store barrier for each oop - // 3) possibly raw copy for any primitive payload trailer - - // addr() points at the payload start, the oop map offset are relative to - // the object header, adjust address to account for this discrepancy. - const address src_addr = src.addr(); - const address dst_addr = dst.addr(); - const address oop_map_adjusted_src_addr = src_addr - md->payload_offset(); - OopMapBlock* map = md->start_of_nonstatic_oop_maps(); - const OopMapBlock* const end = map + md->nonstatic_oop_map_count(); - size_t size_in_bytes = md->layout_size_in_bytes(lk); - size_t copied_bytes = 0; - while (map != end) { - zpointer* src_p = (zpointer*)(oop_map_adjusted_src_addr + map->offset()); - const uintptr_t oop_offset = uintptr_t(src_p) - uintptr_t(src_addr); - zpointer* dst_p = (zpointer*)(uintptr_t(dst_addr) + oop_offset); - - // Copy any leading primitive payload before every cluster of oops - assert(copied_bytes < oop_offset || copied_bytes == oop_offset, "Negative sized leading payload segment"); - copy_primitive_payload(src_addr, dst_addr, oop_offset - copied_bytes, copied_bytes); - - // Copy a cluster of oops - for (const zpointer* const src_end = src_p + map->count(); src_p < src_end; src_p++, dst_p++) { - oop_copy_one(dst_p, src_p); - copied_bytes += sizeof(zpointer); - } - map++; - } + InlineKlass* const klass = src.klass(); - // Copy trailing primitive payload after potential oops - assert(copied_bytes < size_in_bytes || copied_bytes == size_in_bytes, "Negative sized trailing payload segment"); - copy_primitive_payload(src_addr, dst_addr, size_in_bytes - copied_bytes, copied_bytes); - } else { - Raw::value_copy_in_heap(src, dst); - } + const LayoutKind layout_kind = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind()); + const size_t payload_size = klass->layout_size_in_bytes(layout_kind); + + // The addr() points at the payload start, not the object start. + const address src_payload = src.addr(); + const address dst_payload = dst.addr(); + + auto primitive_function = [&](size_t offset, size_t size) { + Copy::copy_value_content(src_payload + offset, dst_payload + offset, size); + }; + + auto oop_function = [&](size_t offset) { + zpointer* const src_p = (zpointer*)(src_payload + offset); + zpointer* const dst_p = (zpointer*)(dst_payload + offset); + const OopCopyResult result = oop_copy_one(dst_p, src_p); + assert(result == OopCopyResult::ok, "Unexpected copy checks"); + }; + + value_primitive_and_oop_iterate( + klass, + dst_payload, + payload_size, + primitive_function, + oop_function); } template inline void ZBarrierSet::AccessBarrier::value_store_null_in_heap(const ValuePayload& dst) { - const LayoutKind lk = dst.layout_kind(); - assert(!LayoutKindHelper::is_null_free_flat(lk), "Cannot store null in null free layout"); - const InlineKlass* md = dst.klass(); - - if (md->contains_oops()) { - assert(!LayoutKindHelper::is_atomic_flat(lk) || - (md->nonstatic_oop_map_count() == 1 && - md->layout_size_in_bytes(lk) == sizeof(zpointer)), - "ZGC can only handle atomic flat values with a single oop"); - - // Iterate over each oop map, performing: - // 1) possibly raw clear for any primitive payload before each map - // 2) store barrier and clear for each oop - // 3) possibly raw clear for any primitive payload trailer - - // addr() points at the payload start, the oop map offset are relative to - // the object header, adjust address to account for this discrepancy. - const address dst_addr = dst.addr(); - const address oop_map_adjusted_dst_addr = dst_addr - md->payload_offset(); - OopMapBlock* map = md->start_of_nonstatic_oop_maps(); - const OopMapBlock* const end = map + md->nonstatic_oop_map_count(); - size_t size_in_bytes = md->layout_size_in_bytes(lk); - size_t copied_bytes = 0; - while (map != end) { - zpointer* dst_p = (zpointer*)(oop_map_adjusted_dst_addr + map->offset()); - const uintptr_t oop_offset = uintptr_t(dst_p) - uintptr_t(dst_addr); - - // Clear any leading primitive payload before every cluster of oops - assert(copied_bytes < oop_offset || copied_bytes == oop_offset, "Negative sized leading payload segment"); - clear_primitive_payload(dst_addr, oop_offset - copied_bytes, copied_bytes); - - // Clear a cluster of oops - for (const zpointer* const dst_end = dst_p + map->count(); dst_p < dst_end; dst_p++) { - oop_clear_one(dst_p); - copied_bytes += sizeof(zpointer); - } - map++; - } + InlineKlass* const klass = dst.klass(); + const LayoutKind layout_kind = dst.layout_kind(); - // Clear trailing primitive payload after potential oops - assert(copied_bytes < size_in_bytes || copied_bytes == size_in_bytes, "Negative sized trailing payload segment"); - clear_primitive_payload(dst_addr, size_in_bytes - copied_bytes, copied_bytes); - } else { + assert(!LayoutKindHelper::is_null_free_flat(layout_kind), + "Cannot store null in null free layout"); + + if (!klass->contains_oops()) { + // All fields are primitives Raw::value_store_null(dst); + return; } + + const size_t payload_size = klass->layout_size_in_bytes(layout_kind); + + // The addr() points at the payload start, not the object start. + const address dst_payload = dst.addr(); + + auto primitive_function = [&](size_t offset, size_t size) { + Copy::clear_value_content(dst_payload + offset, size); + }; + + auto oop_function = [&](size_t offset) { + zpointer* const p = (zpointer*)(dst_payload + offset); + const OopCopyResult result = oop_clear_one(p); + assert(result == OopCopyResult::ok, "Unexpected copy checks"); + }; + + value_primitive_and_oop_iterate(klass, + dst_payload, + payload_size, + primitive_function, + oop_function); } // diff --git a/src/hotspot/share/gc/z/z_globals.hpp b/src/hotspot/share/gc/z/z_globals.hpp index ceffd490ba00..06adba80be03 100644 --- a/src/hotspot/share/gc/z/z_globals.hpp +++ b/src/hotspot/share/gc/z/z_globals.hpp @@ -36,6 +36,7 @@ \ product(double, ZAllocationSpikeTolerance, 2.0, \ "Allocation spike tolerance factor") \ + range(0, INT_MAX) \ \ product(double, ZFragmentationLimit, 5.0, \ "Maximum allowed heap fragmentation") \ diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp index 8fc06c253557..82a4a369a5f7 100644 --- a/src/hotspot/share/memory/heapInspection.cpp +++ b/src/hotspot/share/memory/heapInspection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -597,7 +597,15 @@ void ClassPrintLayout::class_print_layout(outputStream* st, char* class_name) { return; } - Symbol* classname = SymbolTable::probe(class_name, (int)strlen(class_name)); + ResourceMark rm; + char* normalized_name = ResourceArea::strdup(class_name); + for (char* p = normalized_name; *p != '\0'; p++) { + if (*p == JVM_SIGNATURE_DOT) { + *p = JVM_SIGNATURE_SLASH; + } + } + + Symbol* classname = SymbolTable::probe(normalized_name, (int)strlen(normalized_name)); GrowableArray* klasses = new (mtServiceability) GrowableArray(100, mtServiceability); @@ -608,9 +616,9 @@ void ClassPrintLayout::class_print_layout(outputStream* st, char* class_name) { Klass* klass = klasses->at(i); if (!klass->is_instance_klass()) continue; // Skip InstanceKlass* ik = InstanceKlass::cast(klass); - st->print_cr("Class %s [@%s]:", klass->name()->as_C_string(), - klass->class_loader_data()->loader_name()); ResourceMark rm; + st->print_cr("Class %s [@%s]:", klass->external_name(), + klass->class_loader_data()->loader_name()); GrowableArray* fields = new (mtServiceability) GrowableArray(100, mtServiceability); for (AllFieldStream fd(ik); !fd.done(); fd.next()) { if (!fd.access_flags().is_static()) { diff --git a/src/hotspot/share/oops/flatArrayKlass.inline.hpp b/src/hotspot/share/oops/flatArrayKlass.inline.hpp index eb990ddcf40c..11eaa6c78809 100644 --- a/src/hotspot/share/oops/flatArrayKlass.inline.hpp +++ b/src/hotspot/share/oops/flatArrayKlass.inline.hpp @@ -65,7 +65,6 @@ void FlatArrayKlass::oop_oop_iterate_elements_specialized_bounded(flatArrayOop a const int addr_incr = 1 << shift; uintptr_t elem_addr = (uintptr_t)a->base(); uintptr_t stop_addr = elem_addr + ((uintptr_t)a->length() << shift); - const int oop_offset = element_klass()->payload_offset(); if (elem_addr < lo) { uintptr_t diff = lo - elem_addr; @@ -78,7 +77,7 @@ void FlatArrayKlass::oop_oop_iterate_elements_specialized_bounded(flatArrayOop a const uintptr_t end = stop_addr; while (elem_addr < end) { - element_klass()->oop_iterate_specialized_bounded((address)(elem_addr - oop_offset), closure, lo, hi); + element_klass()->oop_iterate_value_payload_bounded((address)elem_addr, closure, lo, hi); elem_addr += addr_incr; } } diff --git a/src/hotspot/share/oops/inlineKlass.cpp b/src/hotspot/share/oops/inlineKlass.cpp index 1ea28e9c6880..b37e1ff80f66 100644 --- a/src/hotspot/share/oops/inlineKlass.cpp +++ b/src/hotspot/share/oops/inlineKlass.cpp @@ -74,8 +74,10 @@ InlineKlass::Members::Members() _nullable_non_atomic_size_in_bytes(-1), _null_marker_offset(-1), _fast_acmp_offset(-1), - _fast_acmp_mask(0) { -} + _fast_acmp_mask(0), + _fast_hashcode_offset(-1), + _fast_hashcode_shift(0) +{} InlineKlass::InlineKlass() { assert(CDSConfig::is_dumping_archive() || UseSharedSpaces, "only for CDS"); @@ -576,6 +578,8 @@ void InlineKlass::Members::print_on(outputStream* st) const { st->print_cr(BULLET"null marker offset: %d", _null_marker_offset); st->print_cr(BULLET"fast acmp offset: %d", _fast_acmp_offset); st->print_cr(BULLET"fast acmp mask: " INT64_FORMAT_X_0, _fast_acmp_mask); + st->print_cr(BULLET"fast hashcode offset: %d", _fast_hashcode_offset); + st->print_cr(BULLET"fast hashcode shift: %d", _fast_hashcode_shift); } #undef BULLET diff --git a/src/hotspot/share/oops/inlineKlass.hpp b/src/hotspot/share/oops/inlineKlass.hpp index 8be85a989e82..97ef09a9d540 100644 --- a/src/hotspot/share/oops/inlineKlass.hpp +++ b/src/hotspot/share/oops/inlineKlass.hpp @@ -105,7 +105,7 @@ class InlineKlass: public InstanceKlass { // // This doesn't always apply, for instance, if there are oops among the fields, we shouldn't carelessly load and compare: // the GC might move the object in between. - // To signal this fast path cannot be done on this current class, simply put 0 in _fast_acmp_mask. + // To signal this fast path cannot be done on this current class, simply put -1 in _fast_acmp_offset. // // We also should take care of not loading further than the object, even if it means reading part of the header. // For this reason, we can't use _payload_offset, but we need our special offset. @@ -114,6 +114,60 @@ class InlineKlass: public InstanceKlass { int _fast_acmp_offset; // if < 0, fast acmp doesn't apply int64_t _fast_acmp_mask; // can be 0 for empty value classes + // When we can't intrinsify the identityHashCode, we can still avoid the Java call at runtime if the value object is nice + // enough. This fast path basically implements the method ValueObjectMethods::valueObjectHashCode in a special case. This + // special case is when there is at most one no-oop segment in the acmp maps, that this segment (if it exists) is 1, 2, 4 + // or 8 byte long, and there is no oop in the acmp maps. Basically, valueObjectHashCode makes 0 or 1 iteration of the big + // outer loop, and one iteration of one of the inner loops. The fast path loads a long at the given offset, isolates the + // numeric value we are interested in, and does the arithmetic. + // + // There are cases: + // 1. hashcode fast path doesn't apply: we set _fast_hashcode_offset < 0 + // 2. the object has no segments (i.e. it is empty): we set _fast_hashcode_offset = 0 + // 3. the object has one segment: we set _fast_hashcode_offset according to where we should load. + // + // Alike for the acmp fast path, we must not load further than the object, and we use the same trick as for acmp, and we + // load possibly some part of the header. The cases 2. and 3. cannot collide since loading at offset 0 would read only the + // header, and no payload. + // + // But unlike acmp, we need the actual arithmetic value, and resetting irrelevant bits is not correct. To do that, we need + // to have a different logic wrt. endianness. Moreover, the fast path needs to handle differently when the segment is 8-byte + // long, just as valueObjectHashCode does, while the arithmetic for segments of size 1, 2 or 4 is the same. This is also known + // by a endianness-dependent test. + int _fast_hashcode_offset; // if < 0, fast hashcode doesn't apply + + // It turns out we need the same helping data for little and big endian at the moment. Yet, the logic is not quite the same. + // + // === LITTLE ENDIAN === + // In little endian, the memory layout, with a 4-byte segment whose value (as returned by getInt) would be 0x01 02 03 04. The + // memory layout of the object would be something like: + // v- start of payload + // ....header.... | 04 03 02 01 + // \___________|___________/ + // Not to load too far, we load at offset "start of payload" - 4, so, we get some header bytes, and we get the long value + // 0x01 02 03 04 HH HH HH HH, where HH are header bytes. To get the integer value, we can simply do an arithmetic right shift, + // by 4 bytes (32 bits) in this case. By doing an arithmetic right shift, we conserve the mathematical value, even if we cut + // higher bits (as long as we leave at least as much as the block we load). + // + // === BIG ENDIAN === + // In big endian, the memory layout, with a 4-byte segment whose value (as returned by getInt) would be 0x01 02 03 04. The + // memory layout of the object would be something like: + // v- start of payload + // ....header.... | 01 02 03 04 + // \___________|___________/ + // Not to load too far, we load at offset "start of payload" - 4, so, we get some header bytes, and we get the long value + // 0xHH HH HH HH 01 02 03 04, where HH are header bytes. To get the integer value, we can simply so a left shift, which + // fills the lower bits with 0, followed by a arithmetic right shift, to preserve the mathematical value. The shift magnitude + // is equal to the number of bits we need to discard. In this example, that is 32. + // + // === COMMON === + // This field is saying by how much we need to shift. Since we keep 1, 2, 4 or 8 bytes, the legal values of _fast_hashcode_shift + // are 8 * (8 - (1, 2, 4, 8)) = 8 * (7, 6, 4, 0) = 56, 48, 32, 0. + // + // The fast path is aware we are loading a long if the shift is 0. + // Value is not specified (and does not matter) if _fast_hashcode_offset <= 0 + int _fast_hashcode_shift; + Members(); void print_on(outputStream* st) const; @@ -212,6 +266,12 @@ class InlineKlass: public InstanceKlass { int64_t fast_acmp_mask() const { return members()._fast_acmp_mask; } void set_fast_acmp_mask(int64_t mask) { members()._fast_acmp_mask = mask; } + int fast_hashcode_offset() const { return members()._fast_hashcode_offset; } + void set_fast_hashcode_offset(int offset) { members()._fast_hashcode_offset = offset; } + + int fast_hashcode_shift() const { return members()._fast_hashcode_shift; } + void set_fast_hashcode_shift(int shift) { members()._fast_hashcode_shift = shift; } + bool supports_nullable_layouts() const { return has_nullable_non_atomic_layout() || has_nullable_atomic_layout(); } @@ -279,12 +339,17 @@ class InlineKlass: public InstanceKlass { bool contains_oops() const { return nonstatic_oop_map_count() > 0; } int nonstatic_oop_count(); - // oop iterate raw inline type data pointer (where oop_addr may not be an oop, but backing/array-element) + // oop iterate the payload of a value object. + // + // * Function: void function(T* p) + template + inline void oop_iterate_value_payload_f(address payload, Function function); + template - inline void oop_iterate_specialized(const address oop_addr, OopClosureType* closure); + inline void oop_iterate_value_payload(address payload, OopClosureType* closure); template - inline void oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, uintptr_t lo, uintptr_t hi); + inline void oop_iterate_value_payload_bounded(address payload, OopClosureType* closure, uintptr_t lo, uintptr_t hi); // Support for the scalarized calling convention void initialize_calling_convention(TRAPS); @@ -334,6 +399,14 @@ class InlineKlass: public InstanceKlass { return byte_offset_of(Members, _fast_acmp_mask); } + static ByteSize fast_hashcode_offset_offset() { + return byte_offset_of(Members, _fast_hashcode_offset); + } + + static ByteSize fast_hashcode_shift_offset() { + return byte_offset_of(Members, _fast_hashcode_shift); + } + oop null_reset_value() const; void set_null_reset_value(oop val); diff --git a/src/hotspot/share/oops/inlineKlass.inline.hpp b/src/hotspot/share/oops/inlineKlass.inline.hpp index 393c07fe9faa..96882dd2f5cd 100644 --- a/src/hotspot/share/oops/inlineKlass.inline.hpp +++ b/src/hotspot/share/oops/inlineKlass.inline.hpp @@ -115,30 +115,47 @@ inline address InlineKlass::payload_addr(oop o) const { return cast_from_oop
    (o) + payload_offset(); } -template -void InlineKlass::oop_iterate_specialized(const address oop_addr, OopClosureType* closure) { +template +void InlineKlass::oop_iterate_value_payload_f(address payload, Function function) { OopMapBlock* map = start_of_nonstatic_oop_maps(); OopMapBlock* const end_map = map + nonstatic_oop_map_count(); + // OopMap offsets are relative to an object header, but we are iterating over + // inlined value payloads, which often don't have an object header. Set up a + // synthetic object base that can be used by the oop map offset calculations. + const address synthetic_object_base = payload - payload_offset(); + for (; map < end_map; map++) { - T* p = (T*) (oop_addr + map->offset()); + T* p = (T*) (synthetic_object_base + map->offset()); T* const end = p + map->count(); for (; p < end; ++p) { - Devirtualizer::do_oop(closure, p); + function(p); } } } template -inline void InlineKlass::oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, uintptr_t lo, uintptr_t hi) { +void InlineKlass::oop_iterate_value_payload(address payload, OopClosureType* closure) { + oop_iterate_value_payload_f(payload, [&](T* p) { + Devirtualizer::do_oop(closure, p); + }); +} + +template +inline void InlineKlass::oop_iterate_value_payload_bounded(address payload, OopClosureType* closure, uintptr_t lo, uintptr_t hi) { OopMapBlock* map = start_of_nonstatic_oop_maps(); OopMapBlock* const end_map = map + nonstatic_oop_map_count(); T* const l = (T*) lo; T* const h = (T*) hi; + // OopMap offsets are relative to an object header, but we are iterating over + // inlined value payloads, which often don't have an object header. Set up a + // synthetic object base that can be used by the oop map offset calculations. + const address synthetic_object_base = payload - payload_offset(); + for (; map < end_map; map++) { - T* p = (T*) (oop_addr + map->offset()); + T* p = (T*) (synthetic_object_base + map->offset()); T* end = p + map->count(); if (p < l) { p = l; diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 0b47b182e520..010510c2f3fd 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -3406,6 +3406,17 @@ Symbol* InstanceKlass::source_file_name() const { return _constant u2 InstanceKlass::source_file_name_index() const { return _constants->source_file_name_index(); } void InstanceKlass::set_source_file_name_index(u2 sourcefile_index) { _constants->set_source_file_name_index(sourcefile_index); } +Symbol* InstanceKlass::source_file_name(int version) const { + // Return the source file name for this version of the classfile, if redefined with RedefineClasses + const InstanceKlass* holder = get_klass_version(version); + if (holder == nullptr) { + // Redefined previous class has been cleaned up. + return nullptr; + } else { + return holder->source_file_name(); + } +} + // minor and major version numbers of class file u2 InstanceKlass::minor_version() const { return _constants->minor_version(); } void InstanceKlass::set_minor_version(u2 minor_version) { _constants->set_minor_version(minor_version); } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index aba81900985a..768da36dc4be 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -771,6 +771,7 @@ class InstanceKlass: public Klass { Symbol* source_file_name() const; u2 source_file_name_index() const; void set_source_file_name_index(u2 sourcefile_index); + Symbol* source_file_name(int version) const; // minor and major version numbers of class file u2 minor_version() const; diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index 7d403660f8b8..2201633b160a 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -57,14 +57,6 @@ void markWord::print_on(outputStream* st, bool print_monitor_info) const { // have to check has_monitor() before is_locked() // Valhalla: inline types/arrays can't be monitored st->print(" monitor(" INTPTR_FORMAT ")=", value()); - if (print_monitor_info && !UseObjectMonitorTable) { - ObjectMonitor* mon = monitor(); - if (mon == nullptr) { - st->print("null (this should never be seen!)"); - } else { - mon->print_on(st); - } - } } else if (is_locked()) { // last bits != 01 => 00 // thin locked // Valhalla: inline types can not possess an object monitor diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 789d206f57c4..42ced7df0b2d 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.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 @@ -30,6 +30,7 @@ #include "oops/compressedKlass.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/globals.hpp" +#include "utilities/debug.hpp" #include "utilities/powerOfTwo.hpp" // The markWord describes the header of an object. @@ -53,8 +54,7 @@ // // [header | 00] locked locked regular object header (fast-locking in use) // [header | 01] unlocked regular object header -// [header | 10] monitor inflated lock (UseObjectMonitorTable == true) -// [ptr | 10] monitor inflated lock (UseObjectMonitorTable == false, header is swapped out) +// [header | 10] monitor inflated lock // [ptr | 11] marked used to mark an object (header is swapped out) // // - self-fwd - used by some GCs to indicate in-place forwarding. @@ -254,21 +254,19 @@ class markWord { return markWord((value() & ~lock_mask_in_place) | monitor_value); } ObjectMonitor* monitor() const { - assert(has_monitor(), "check"); - assert(!UseObjectMonitorTable, "Locking with OM table does not use markWord for monitors"); - // Use xor instead of &~ to provide one extra tag-bit check. - return (ObjectMonitor*) (value() ^ monitor_value); + // Locking with OM table does not use markWord for monitors. + ShouldNotCallThis(); + return (ObjectMonitor*) nullptr; } static markWord encode(ObjectMonitor* monitor) { - assert(!UseObjectMonitorTable, "Locking with OM table does not use markWord for monitors"); - uintptr_t tmp = (uintptr_t) monitor; - return markWord(tmp | monitor_value); + // Locking with OM table does not use markWord for monitors. + ShouldNotCallThis(); + return markWord(0); } bool has_monitor_pointer() const { - intptr_t lockbits = value() & lock_mask_in_place; - return !UseObjectMonitorTable && lockbits == monitor_value; + return false; // Locking with OM table does not use markWord for monitors. } bool has_displaced_mark_helper() const { diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp index 67e5e474ccec..6033b4e44490 100644 --- a/src/hotspot/share/oops/stackChunkOop.cpp +++ b/src/hotspot/share/oops/stackChunkOop.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 @@ -534,6 +534,15 @@ class VerifyStackChunkFrameClosure { assert(num_oops >= 0, ""); _argsize = f.stack_argsize() + frame::metadata_words_at_top; + if (f.is_compiled()) { + int real_frame_size = 0; + frame fr = f.to_frame(); + if (fr.was_augmented_on_entry(real_frame_size)) { + // Extended frames exclude stack arguments passed by caller as they are + // never accessed. For interpreted callers they are discarded when freezing. + _argsize = 0; + } + } _size += fsize; _num_oops += num_oops; if (f.is_interpreted()) { diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp index 8c8e8521fbb1..2b87c899fe16 100644 --- a/src/hotspot/share/oops/trainingData.cpp +++ b/src/hotspot/share/oops/trainingData.cpp @@ -120,7 +120,7 @@ void TrainingData::verify() { } static bool is_excluded(InstanceKlass* k) { - if (!k->is_loaded() || k->has_been_redefined()) { + if (!k->is_loaded() || (TrainingData::need_data() && k->has_been_redefined())) { return true; } if (CDSConfig::is_at_aot_safepoint()) { diff --git a/src/hotspot/share/oops/typeArrayOop.hpp b/src/hotspot/share/oops/typeArrayOop.hpp index e5750052541b..de020920db3b 100644 --- a/src/hotspot/share/oops/typeArrayOop.hpp +++ b/src/hotspot/share/oops/typeArrayOop.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 @@ -108,9 +108,6 @@ class typeArrayOopDesc : public arrayOopDesc { jbyte byte_at_acquire(int which) const; void release_byte_at_put(int which, jbyte contents); - Symbol* symbol_at(int which) const; - void symbol_at_put(int which, Symbol* contents); - // Sizing // Returns the number of words necessary to hold an array of "len" diff --git a/src/hotspot/share/oops/typeArrayOop.inline.hpp b/src/hotspot/share/oops/typeArrayOop.inline.hpp index c431e3db16d6..61ffd33706d7 100644 --- a/src/hotspot/share/oops/typeArrayOop.inline.hpp +++ b/src/hotspot/share/oops/typeArrayOop.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -162,25 +162,4 @@ inline void typeArrayOopDesc::release_byte_at_put(int which, jbyte contents) { AtomicAccess::release_store(byte_at_addr(which), contents); } -// Java thinks Symbol arrays are just arrays of either long or int, since -// there doesn't seem to be T_ADDRESS, so this is a bit of unfortunate -// casting -#ifdef _LP64 -inline Symbol* typeArrayOopDesc::symbol_at(int which) const { - return *reinterpret_cast(long_at_addr(which)); -} - -inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) { - *reinterpret_cast(long_at_addr(which)) = contents; -} -#else -inline Symbol* typeArrayOopDesc::symbol_at(int which) const { - return *reinterpret_cast(int_at_addr(which)); -} -inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) { - *reinterpret_cast(int_at_addr(which)) = contents; -} -#endif // _LP64 - - #endif // SHARE_OOPS_TYPEARRAYOOP_INLINE_HPP diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 3916b20f59ac..d2926f4ebb98 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -37,6 +37,7 @@ #include "opto/convertnode.hpp" #include "opto/escape.hpp" #include "opto/inlinetypenode.hpp" +#include "opto/library_call.hpp" #include "opto/locknode.hpp" #include "opto/machnode.hpp" #include "opto/matcher.hpp" @@ -1195,14 +1196,22 @@ Node* CallStaticJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) { } } - // Try to replace the runtime call to the substitutability test emitted by acmp if we can reason - // about the operands - if (can_reshape && !control()->is_top() && !memory()->is_top() && method() != nullptr && - method()->holder() == phase->C->env()->ValueObjectMethods_klass() && - method()->name() == ciSymbols::isSubstitutable_name()) { - Node* res = replace_is_substitutable(phase->is_IterGVN()); - if (res != nullptr) { - return res; + if (can_reshape && !control()->is_top() && !memory()->is_top() && method() != nullptr) { + if (method()->holder() == phase->C->env()->ValueObjectMethods_klass() && + method()->name() == ciSymbols::isSubstitutable_name()) { + // Try to replace the runtime call to the substitutability test emitted by acmp if we can reason + // about the operands + Node* res = replace_is_substitutable(phase->is_IterGVN()); + if (res != nullptr) { + return res; + } + } else if (method()->holder() == phase->C->env()->System_klass() && + method()->name() == ciSymbols::identityHashCode_name()) { + // Same with identityHashCode + Node* res = replace_identity_hash_code(phase->is_IterGVN()); + if (res != nullptr) { + return res; + } } } @@ -1398,6 +1407,70 @@ bool CallStaticJavaNode::remove_unknown_flat_array_load(PhaseIterGVN* igvn, Node return true; } +Node* CallStaticJavaNode::replace_identity_hash_code(PhaseIterGVN* igvn) { + Node* arg = in(TypeFunc::Parms); + intptr_t klass_hash; + if (!InlineTypeNode::can_emit_identity_hash_code(*igvn, arg, klass_hash)) { + // We can't expand, but now, maybe we can also tell the fast path won't work + const Type* arg_type = igvn->type(arg); + if (UseHashcodeFastPath && igvn->type(arg)->is_inlinetypeptr()) { + ciInlineKlass* vk = arg_type->inline_klass(); + bool fast_path_wont_work = false; + fast_path_wont_work = fast_path_wont_work || vk->number_of_oop_entries_in_acmp_map() > 0; + fast_path_wont_work = fast_path_wont_work || vk->number_of_nonoop_entries_in_acmp_map() > 1; + if (vk->number_of_nonoop_entries_in_acmp_map() == 1) { + int size = vk->get_nonoop_segment_of_acmp_map(0)._size; + fast_path_wont_work = fast_path_wont_work || (size != 1 && size != 2 && size != 4 && size != 8); + } + if (fast_path_wont_work) { + IfNode* fast_path_if = LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(igvn, this); + if (fast_path_if != nullptr) { + fast_path_if->set_req(1, igvn->intcon(1)); + igvn->_worklist.push(fast_path_if); + return this; + } + } + } + return nullptr; + } + + // Delay IGVN during macro expansion + assert(!igvn->delay_transform(), "must not delay during Ideal"); + igvn->set_delay_transform(true); + GraphKit kit(this, *igvn); + + Node* replace = InlineTypeNode::emit_identity_hash_code(&kit, arg, klass_hash); + igvn->set_delay_transform(false); + assert(replace != nullptr, "must succeed"); + + if (UseHashcodeFastPath) { + // Sabotage the fast hashcode path + IfNode* fast_path_if = LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(igvn, this); + if (fast_path_if != nullptr) { + fast_path_if->set_req(1, igvn->intcon(1)); + igvn->_worklist.push(fast_path_if); + } + } + + // Kill exception projections and return a tuple that will replace the call + CallProjections* projs = extract_projections(false /*separate_io_proj*/); + if (projs->fallthrough_catchproj != nullptr) { + igvn->replace_node(projs->fallthrough_catchproj, kit.control()); + } + if (projs->catchall_memproj != nullptr) { + igvn->replace_node(projs->catchall_memproj, igvn->C->top()); + } + if (projs->catchall_ioproj != nullptr) { + igvn->replace_node(projs->catchall_ioproj, igvn->C->top()); + } + if (projs->catchall_catchproj != nullptr) { + igvn->replace_node(projs->catchall_catchproj, igvn->C->top()); + } + Node* new_mem = kit.reset_memory(); + assert(in(TypeFunc::Memory) == new_mem, "must not modify memory"); + return TupleNode::make(tf()->range_cc(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace); +} + // Try to replace a runtime call to the substitutability test by either a simple pointer comparison // if either operand is not a value object, or comparing their fields if either operand is an // object of a known value type diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 465d1d429a26..229108614f36 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -905,6 +905,7 @@ class CallStaticJavaNode : public CallJavaNode { bool remove_unknown_flat_array_load(PhaseIterGVN* igvn, Node* ctl, Node* mem, Node* unc_arg); Node* replace_is_substitutable(PhaseIterGVN* igvn); + Node* replace_identity_hash_code(PhaseIterGVN* igvn); public: CallStaticJavaNode(Compile* C, const TypeFunc* tf, address addr, ciMethod* method) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 8e92471d4dfa..e12eec300899 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1413,7 +1413,7 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { // Process weird unsafe references. if (offset == Type::OffsetBot && (tj->isa_instptr() /*|| tj->isa_klassptr()*/)) { - assert(InlineUnsafeOps || StressReflectiveCode || UseAcmpFastPath, "indeterminate pointers come only from unsafe ops"); + assert(InlineUnsafeOps || StressReflectiveCode || UseAcmpFastPath || UseHashcodeFastPath, "indeterminate pointers come only from unsafe ops"); assert(!is_known_inst, "scalarizable allocation should not have unsafe references"); tj = TypeOopPtr::BOTTOM; ptr = tj->ptr(); diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index cb66ba0bdb64..957b79caf1fe 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -399,7 +399,7 @@ bool ConnectionGraph::compute_escape() { if (VerifyReduceAllocationMerges) { for (uint i = 0; i < reducible_merges.size(); i++ ) { Node* n = reducible_merges.at(i); - if (!can_reduce_phi(n->as_Phi())) { + if (n->outcnt() > 0 && !can_reduce_phi(n->as_Phi())) { TraceReduceAllocationMerges = true; n->dump(2); n->dump(-2); @@ -666,7 +666,7 @@ bool ConnectionGraph::can_reduce_phi(PhiNode* ophi) const { // If there was an error attempting to reduce allocation merges for this // method we might have disabled the compilation and be retrying with RAM // disabled. - if (!_compile->do_reduce_allocation_merges() || ophi->region()->Opcode() != Op_Region) { + if (!_compile->do_reduce_allocation_merges() || ophi->region() == nullptr || ophi->region()->Opcode() != Op_Region) { return false; } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index e41beaa26e0d..a2da4efa2a1b 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -46,17 +46,14 @@ #include "opto/intrinsicnode.hpp" #include "opto/locknode.hpp" #include "opto/machnode.hpp" -#include "opto/memnode.hpp" #include "opto/multnode.hpp" #include "opto/narrowptrnode.hpp" #include "opto/opaquenode.hpp" -#include "opto/opcodes.hpp" #include "opto/parse.hpp" #include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subtypenode.hpp" -#include "opto/type.hpp" #include "runtime/arguments.hpp" #include "runtime/deoptimization.hpp" #include "runtime/sharedRuntime.hpp" @@ -4862,81 +4859,51 @@ void GraphKit::store_String_coder(Node* str, Node* value) { value, TypeInt::BYTE, T_BYTE, IN_HEAP | MO_UNORDERED); } -// If input and output memory types differ, capture the whole memory to preserve -// the dependency between preceding and subsequent loads/stores. -// For example, the following program: -// StoreB -// compress_string -// LoadB -// has this memory graph (use->def): -// LoadB -> compress_string -> CharMem -// ... -> StoreB -> ByteMem -// The intrinsic hides the dependency between LoadB and StoreB, causing -// the load to read from memory not containing the result of the StoreB. -// The correct memory graph should look like this: -// LoadB -> compress_string -> MergeMem -> StoreB -Node* GraphKit::capture_memory(const TypePtr*& combined_type, const TypePtr* src_type, const TypePtr* dst_type) { +// Capture src and dst memory state with a MergeMemNode +Node* GraphKit::capture_memory(const TypePtr* src_type, const TypePtr* dst_type) { if (src_type == dst_type) { // Types are equal, we don't need a MergeMemNode - combined_type = src_type; return memory(src_type); } - Node* mem = reset_memory(); - set_all_memory(mem); - combined_type = TypePtr::BOTTOM; - return mem; -} - -// If dst_type and src_type are different, str may have an anti-dependency with another node -// consuming src_type. -// For example: -// compress_string -// StoreC -// has this memory graph (use->def): -// compress_string -> MergeMem -> CharMem -// StoreC -// The scheduler needs to ensure that compress_string is not executed after StoreC, or it will read -// the wrong memory. For normal loads, the scheduler computes its anti-dependencies to ensure the -// memory it reads from is not killed. Since we do not compute anti-dependencies for -// StrCompressedCopyNode, manually insert a MemBar so the anti-dependency becomes use-def -// dependency: -// StoreC -> MemBar -> MergeMem -> compress_string -> MergeMem -> CharMem -// --------------------------------> -void GraphKit::memory_effect(Node* res_mem, const TypePtr* src_type, const TypePtr* dst_type) { - set_memory(res_mem, dst_type); - if (src_type != dst_type) { - Node* all_mem = reset_memory(); - set_all_memory(all_mem); - Node* membar = new MemBarCPUOrderNode(C, C->get_alias_index(src_type), nullptr); - membar->init_req(TypeFunc::Control, control()); - membar->init_req(TypeFunc::Memory, all_mem); - membar = _gvn.transform(membar); - set_control(_gvn.transform(new ProjNode(membar, TypeFunc::Control))); - set_memory(_gvn.transform(new ProjNode(membar, TypeFunc::Memory)), src_type); - } + MergeMemNode* merge = MergeMemNode::make(map()->memory()); + record_for_igvn(merge); // fold it up later, if possible + int src_idx = C->get_alias_index(src_type); + int dst_idx = C->get_alias_index(dst_type); + merge->set_memory_at(src_idx, memory(src_idx)); + merge->set_memory_at(dst_idx, memory(dst_idx)); + return merge; } Node* GraphKit::compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count) { assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported"); assert(src_type == TypeAryPtr::BYTES || src_type == TypeAryPtr::CHARS, "invalid source type"); - const TypePtr* dst_type = TypeAryPtr::BYTES; - const TypePtr* adr_type; - Node* mem = capture_memory(adr_type, src_type, dst_type); - StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, adr_type, src, dst, count); + // If input and output memory types differ, capture both states to preserve + // the dependency between preceding and subsequent loads/stores. + // For example, the following program: + // StoreB + // compress_string + // LoadB + // has this memory graph (use->def): + // LoadB -> compress_string -> CharMem + // ... -> StoreB -> ByteMem + // The intrinsic hides the dependency between LoadB and StoreB, causing + // the load to read from memory not containing the result of the StoreB. + // The correct memory graph should look like this: + // LoadB -> compress_string -> MergeMem(CharMem, StoreB(ByteMem)) + Node* mem = capture_memory(src_type, TypeAryPtr::BYTES); + StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, src, dst, count); Node* res_mem = _gvn.transform(new SCMemProjNode(_gvn.transform(str))); - memory_effect(res_mem, src_type, dst_type); + set_memory(res_mem, TypeAryPtr::BYTES); return str; } void GraphKit::inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count) { assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported"); assert(dst_type == TypeAryPtr::BYTES || dst_type == TypeAryPtr::CHARS, "invalid dest type"); - const TypePtr* src_type = TypeAryPtr::BYTES; - const TypePtr* adr_type; - Node* mem = capture_memory(adr_type, src_type, dst_type); - StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, adr_type, src, dst, count); - Node* res_mem = _gvn.transform(str); - memory_effect(res_mem, src_type, dst_type); + // Capture src and dst memory (see comment in 'compress_string'). + Node* mem = capture_memory(TypeAryPtr::BYTES, dst_type); + StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, src, dst, count); + set_memory(_gvn.transform(str), dst_type); } void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) { diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 9310a721dc60..59a95baa5e76 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -345,6 +345,7 @@ class GraphKit : public Phase { Node* DivI(Node* ctl, Node* l, Node* r) { return _gvn.transform(new DivINode(ctl, l, r)); } Node* AndI(Node* l, Node* r) { return _gvn.transform(new AndINode(l, r)); } + Node* AndL(Node* l, Node* r) { return _gvn.transform(new AndLNode(l, r)); } Node* OrI(Node* l, Node* r) { return _gvn.transform(new OrINode(l, r)); } Node* XorI(Node* l, Node* r) { return _gvn.transform(new XorINode(l, r)); } @@ -352,13 +353,20 @@ class GraphKit : public Phase { Node* MinI(Node* l, Node* r) { return _gvn.transform(new MinINode(l, r)); } Node* LShiftI(Node* l, Node* r) { return _gvn.transform(new LShiftINode(l, r)); } + Node* LShiftL(Node* l, Node* r) { return _gvn.transform(new LShiftLNode(l, r)); } Node* RShiftI(Node* l, Node* r) { return _gvn.transform(new RShiftINode(l, r)); } + Node* RShiftL(Node* l, Node* r) { return _gvn.transform(new RShiftLNode(l, r)); } Node* URShiftI(Node* l, Node* r) { return _gvn.transform(new URShiftINode(l, r)); } + Node* URShiftL(Node* l, Node* r) { return _gvn.transform(new URShiftLNode(l, r)); } + Node* URShiftX(Node* l, Node* r) { return _gvn.transform(new URShiftXNode(l, r)); } Node* CmpI(Node* l, Node* r) { return _gvn.transform(new CmpINode(l, r)); } Node* CmpL(Node* l, Node* r) { return _gvn.transform(new CmpLNode(l, r)); } Node* CmpP(Node* l, Node* r) { return _gvn.transform(new CmpPNode(l, r)); } Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new BoolNode(cmp, relop)); } + Node* BoolCmpI(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpI(l, r), relop); } + Node* BoolCmpL(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpL(l, r), relop); } + Node* BoolCmpP(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpP(l, r), relop); } Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(AddPNode::make_with_base(b, a, o)); } @@ -878,8 +886,7 @@ class GraphKit : public Phase { Node* load_String_coder(Node* str, bool set_ctrl); void store_String_value(Node* str, Node* value); void store_String_coder(Node* str, Node* value); - Node* capture_memory(const TypePtr*& combined_type, const TypePtr* src_type, const TypePtr* dst_type); - void memory_effect(Node* res_mem, const TypePtr* src_type, const TypePtr* dst_type); + Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type); Node* compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count); void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count); void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count); diff --git a/src/hotspot/share/opto/inlinetypenode.cpp b/src/hotspot/share/opto/inlinetypenode.cpp index 2f550fec89fe..48dc5ec1562e 100644 --- a/src/hotspot/share/opto/inlinetypenode.cpp +++ b/src/hotspot/share/opto/inlinetypenode.cpp @@ -1021,6 +1021,115 @@ Node* InlineTypeNode::emit_substitutability_check(GraphKit* kit, Node* lhs, Node return result; } +// Check if identityHashCode of 'arg' can be implemented in IR. +// Set klass_hash to be used as the seed of the hash. It might not be available later, +// and we want emit_identity_hash_code not to fail on that. +bool InlineTypeNode::can_emit_identity_hash_code(const PhaseIterGVN& igvn, Node* arg, intptr_t& klass_hash) { + const Type* arg_type = igvn.type(arg); + if (arg_type == TypePtr::NULL_PTR) { + return true; + } + if (!arg_type->is_inlinetypeptr()) { + return false; + } + ciInlineKlass* vk = arg_type->inline_klass(); + if (vk->number_of_oop_entries_in_acmp_map() > 0) { + return false; + } + klass_hash = vk->java_mirror()->hash(); + if (klass_hash == markWord::no_hash) { + return false; + } + return true; +} + +Node* InlineTypeNode::emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t klass_hash) { + if (!kit->C->allow_macro_nodes()) { + // After macro expansion, InlineTypeNodes are also eliminated, creation of new ones then is not + // allowed + return nullptr; + } + PhaseIterGVN& igvn = *kit->gvn().is_IterGVN(); + + RegionNode* region = new RegionNode(1); + PhiNode* phi_result = new PhiNode(region, TypeInt::INT); + igvn.register_new_node_with_optimizer(region); + igvn.register_new_node_with_optimizer(phi_result); + + Node* null_ctl = kit->top(); + arg = kit->null_check_oop(arg, &null_ctl, false, false, false); + if (!null_ctl->is_top()) { + region->add_req(null_ctl); + phi_result->add_req(kit->intcon(0)); + } + const Type* arg_type = igvn.type(arg); + if (arg_type->empty()) { + kit->set_control(region); + return phi_result; + } + + assert(arg_type->is_inlinetypeptr(), "should be a value object at this point"); + assert(!arg_type->maybe_null(), "must check null beforehand"); + ciInlineKlass* vk = arg_type->inline_klass(); + int number_of_nonoop_entries = vk->number_of_nonoop_entries_in_acmp_map(); + assert(vk->number_of_oop_entries_in_acmp_map() == 0, "cannot have oops here"); + + auto make_load = [&](int offset, const Type* type, BasicType bt) -> Node* { + Node* adr = kit->basic_plus_adr(arg, offset); + ciField* field = vk->get_field_by_offset(offset, false); + // If the load is by chance not a mismatch, let's mark it so. This way, loading the field can be simplified + bool is_mismatch = field == nullptr || field->type()->basic_type() != bt; + if (bt == T_BYTE && field != nullptr && field->type()->basic_type() == T_BOOLEAN) { + is_mismatch = false; + bt = T_BOOLEAN; + type = TypeInt::BOOL; + } + return kit->make_load(kit->control(), adr, type, bt, MemNode::unordered, LoadNode::DependsOnlyOnTest, false, false, is_mismatch, is_mismatch); + }; + + Node* const thirty_one = kit->intcon(31); + Node* result = kit->intcon(checked_cast(klass_hash)); + for (int i = 0; i < number_of_nonoop_entries; i++) { + AcmpMapSegment segment = vk->get_nonoop_segment_of_acmp_map(i); + int offset = segment._offset; + int size = segment._size; + int nlong = size / 8; + for (int j = 0; j < nlong; j++) { + Node* la = make_load(offset, TypeLong::LONG, T_LONG); + result = kit->AddI(kit->MulI(thirty_one, result), kit->ConvL2I(la)); + result = kit->AddI(kit->MulI(thirty_one, result), kit->ConvL2I(kit->URShiftL(la, kit->intcon(32)))); + offset += 8; + } + size -= nlong * 8; + int nint = size / 4; + for (int j = 0; j < nint; j++) { + Node* ia = make_load(offset, TypeInt::INT, T_INT); + result = kit->AddI(kit->MulI(thirty_one, result), ia); + offset += 4; + } + size -= nint * 4; + int nshort = size / 2; + for (int j = 0; j < nshort; j++) { + Node* sa = make_load(offset, TypeInt::SHORT, T_SHORT); + result = kit->AddI(kit->MulI(thirty_one, result), sa); + offset += 2; + } + size -= nshort * 2; + for (int j = 0; j < size; j++) { + Node* ba = make_load(offset, TypeInt::BYTE, T_BYTE); + result = kit->AddI(kit->MulI(thirty_one, result), ba); + offset++; + } + } + result = kit->AndI(result, kit->intcon(markWord::hash_mask)); + + region->add_req(kit->control()); + phi_result->add_req(result); + + kit->set_control(region); + return phi_result; +} + InlineTypeNode* InlineTypeNode::buffer(GraphKit* kit, bool safe_for_replace) { if (is_allocated(&kit->gvn())) { // Already buffered diff --git a/src/hotspot/share/opto/inlinetypenode.hpp b/src/hotspot/share/opto/inlinetypenode.hpp index 3c1dea99ba60..1ef4ebd007b0 100644 --- a/src/hotspot/share/opto/inlinetypenode.hpp +++ b/src/hotspot/share/opto/inlinetypenode.hpp @@ -135,6 +135,10 @@ class InlineTypeNode : public TypeNode { static bool can_emit_substitutability_check(PhaseGVN* phase, Node* lhs, Node* rhs); static Node* emit_substitutability_check(GraphKit* kit, Node* lhs, Node* rhs); + // Implementation of identityHashCode for value classes with restrictions (e.g. no oops) + static bool can_emit_identity_hash_code(const PhaseIterGVN& igvn, Node* arg, intptr_t& klass_hash); + static Node* emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t klass_hash); + // Allocates the inline type (if not yet allocated) InlineTypeNode* buffer(GraphKit* kit, bool safe_for_replace = true); bool is_allocated(PhaseGVN* phase) const; diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 887681233f16..d3e62dacfe80 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -63,6 +63,8 @@ const Type* StrIntrinsicNode::Value(PhaseGVN* phase) const { return bottom_type(); } +uint StrIntrinsicNode::size_of() const { return sizeof(*this); } + //============================================================================= //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 1fe61cfb1785..d81e7bed7e96 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -48,7 +48,7 @@ class PartialSubtypeCheckNode : public Node { //------------------------------StrIntrinsic------------------------------- // Base class for Ideal nodes used in String intrinsic code. -class StrIntrinsicNode : public Node { +class StrIntrinsicNode: public Node { public: // Possible encodings of the parameters passed to the string intrinsic. // 'L' stands for Latin1 and 'U' stands for UTF16. For example, 'LU' means that @@ -59,11 +59,7 @@ class StrIntrinsicNode : public Node { protected: // Encoding of strings. Used to select the right version of the intrinsic. const ArgEncoding _encoding; - virtual uint size_of() const override { return sizeof(StrIntrinsicNode); } - virtual uint hash() const override { return Node::hash() + _encoding; } - virtual bool cmp(const Node& n) const override { - return Node::cmp(n) && _encoding == static_cast(n)._encoding; - } + virtual uint size_of() const; public: StrIntrinsicNode(Node* control, Node* char_array_mem, @@ -81,189 +77,141 @@ class StrIntrinsicNode : public Node { Node(control, char_array_mem, s1, s2), _encoding(encoding) { } - virtual const TypePtr* adr_type() const override = 0; - virtual uint match_edge(uint idx) const override; - virtual uint ideal_reg() const override { return Op_RegI; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; - virtual const Type* Value(PhaseGVN* phase) const override; + virtual const TypePtr* adr_type() const { return TypeAryPtr::BYTES; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; ArgEncoding encoding() const { return _encoding; } private: - virtual bool depends_only_on_test_impl() const override { return false; } + virtual bool depends_only_on_test_impl() const { return false; } }; //------------------------------StrComp------------------------------------- -class StrCompNode final : public StrIntrinsicNode { +class StrCompNode: public StrIntrinsicNode { public: StrCompNode(Node* control, Node* char_array_mem, Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding): StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } }; //------------------------------StrEquals------------------------------------- -class StrEqualsNode final : public StrIntrinsicNode { +class StrEqualsNode: public StrIntrinsicNode { public: StrEqualsNode(Node* control, Node* char_array_mem, Node* s1, Node* s2, Node* c, ArgEncoding encoding): StrIntrinsicNode(control, char_array_mem, s1, s2, c, encoding) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::BOOL; } - virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; //------------------------------StrIndexOf------------------------------------- -class StrIndexOfNode final : public StrIntrinsicNode { +class StrIndexOfNode: public StrIntrinsicNode { public: StrIndexOfNode(Node* control, Node* char_array_mem, Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding): StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } }; //------------------------------StrIndexOfChar------------------------------------- -class StrIndexOfCharNode final : public StrIntrinsicNode { +class StrIndexOfCharNode: public StrIntrinsicNode { public: StrIndexOfCharNode(Node* control, Node* char_array_mem, Node* s1, Node* c1, Node* c, ArgEncoding encoding): StrIntrinsicNode(control, char_array_mem, s1, c1, c, encoding) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } }; //--------------------------StrCompressedCopy------------------------------- -class StrCompressedCopyNode final : public StrIntrinsicNode { -private: - const TypePtr* const _adr_type; - -public: - StrCompressedCopyNode(Node* control, Node* arymem, const TypePtr* adr_type, +class StrCompressedCopyNode: public StrIntrinsicNode { + public: + StrCompressedCopyNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): - StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; - -private: - virtual uint size_of() const override { return sizeof(StrCompressedCopyNode); } - virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _adr_type; } - virtual bool cmp(const Node& n) const override { - return StrIntrinsicNode::cmp(n) && _adr_type == static_cast(n)._adr_type; - } - virtual const TypePtr* adr_type() const override { return _adr_type; } + StrIntrinsicNode(control, arymem, s1, s2, c, none) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //--------------------------StrInflatedCopy--------------------------------- -class StrInflatedCopyNode final : public StrIntrinsicNode { -private: - const TypePtr* const _adr_type; - -public: - StrInflatedCopyNode(Node* control, Node* arymem, const TypePtr* adr_type, +class StrInflatedCopyNode: public StrIntrinsicNode { + public: + StrInflatedCopyNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): - StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return Type::MEMORY; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; - -private: - virtual uint size_of() const override { return sizeof(StrInflatedCopyNode); } - virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _adr_type; } - virtual bool cmp(const Node& n) const override { - return StrIntrinsicNode::cmp(n) && _adr_type == static_cast(n)._adr_type; - } - virtual const TypePtr* adr_type() const override { return _adr_type; } + StrIntrinsicNode(control, arymem, s1, s2, c, none) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return Type::MEMORY; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; //------------------------------AryEq--------------------------------------- -class AryEqNode final : public StrIntrinsicNode { -private: - const TypeAryPtr* const _in_adr_type; - -public: - AryEqNode(Node* control, Node* char_array_mem, const TypeAryPtr* in_adr_type, +class AryEqNode: public StrIntrinsicNode { + public: + AryEqNode(Node* control, Node* char_array_mem, Node* s1, Node* s2, ArgEncoding encoding): - StrIntrinsicNode(control, char_array_mem, s1, s2, encoding), _in_adr_type(in_adr_type) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::BOOL; } - -private: - virtual uint size_of() const override { return sizeof(AryEqNode); } - virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _in_adr_type; } - virtual bool cmp(const Node& n) const override { - return StrIntrinsicNode::cmp(n) && _in_adr_type == static_cast(n)._in_adr_type; - } - virtual const TypePtr* adr_type() const override { return _in_adr_type; } + StrIntrinsicNode(control, char_array_mem, s1, s2, encoding) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; //------------------------------CountPositives------------------------------ -class CountPositivesNode final : public StrIntrinsicNode { +class CountPositivesNode: public StrIntrinsicNode { public: CountPositivesNode(Node* control, Node* char_array_mem, Node* s1, Node* c1): StrIntrinsicNode(control, char_array_mem, s1, c1, none) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::POS; } - virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; } + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::POS; } }; //------------------------------VectorizedHashCodeNode---------------------- -class VectorizedHashCodeNode final : public Node { -private: - const TypeAryPtr* const _in_adr_type; - -public: - VectorizedHashCodeNode(Node* control, Node* ary_mem, const TypeAryPtr* in_adr_type, Node* arg1, Node* cnt1, Node* result, Node* basic_type) - : Node(control, ary_mem, arg1, cnt1, result, basic_type), _in_adr_type(in_adr_type) {}; - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual uint match_edge(uint idx) const override; - virtual uint ideal_reg() const override { return Op_RegI; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; - virtual const Type* Value(PhaseGVN* phase) const override; +class VectorizedHashCodeNode: public Node { + public: + VectorizedHashCodeNode(Node* control, Node* ary_mem, Node* arg1, Node* cnt1, Node* result, Node* basic_type) + : Node(control, ary_mem, arg1, cnt1, result, basic_type) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; private: - virtual uint size_of() const override { return sizeof(VectorizedHashCodeNode); } - virtual uint hash() const override { return Node::hash() + (uint)(uintptr_t) _in_adr_type; } - virtual bool cmp(const Node& n) const override { - return Node::cmp(n) && _in_adr_type == static_cast(n)._in_adr_type; - } - virtual const TypePtr* adr_type() const override { return _in_adr_type; } - virtual bool depends_only_on_test_impl() const override { return false; } + virtual bool depends_only_on_test_impl() const { return false; } }; //------------------------------EncodeISOArray-------------------------------- // encode char[] to byte[] in ISO_8859_1 or ASCII -class EncodeISOArrayNode final : public Node { -private: - const TypePtr* const _adr_type; +class EncodeISOArrayNode: public Node { bool _ascii; - -public: - EncodeISOArrayNode(Node* control, Node* arymem, const TypePtr* adr_type, Node* s1, Node* s2, Node* c, bool ascii) - : Node(control, arymem, s1, s2, c), _adr_type(adr_type), _ascii(ascii) {} + public: + EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii) + : Node(control, arymem, s1, s2, c), _ascii(ascii) {} bool is_ascii() { return _ascii; } - virtual int Opcode() const override; - virtual const Type* bottom_type() const override { return TypeInt::INT; } - virtual uint match_edge(uint idx) const override; - virtual uint ideal_reg() const override { return Op_RegI; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override; - virtual const Type* Value(PhaseGVN* phase) const override; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; + virtual uint size_of() const { return sizeof(EncodeISOArrayNode); } + virtual uint hash() const { return Node::hash() + _ascii; } + virtual bool cmp(const Node& n) const { + return Node::cmp(n) && _ascii == ((EncodeISOArrayNode&)n).is_ascii(); + } private: - virtual uint size_of() const override { return sizeof(EncodeISOArrayNode); } - virtual uint hash() const override { return Node::hash() + (uint)(uintptr_t) _adr_type + _ascii; } - virtual bool cmp(const Node& n) const override { - const EncodeISOArrayNode& e = static_cast(n); - return Node::cmp(n) && _ascii == e._ascii && _adr_type == e._adr_type; - } - virtual const TypePtr* adr_type() const override { return _adr_type; } - virtual bool depends_only_on_test_impl() const override { return false; } + virtual bool depends_only_on_test_impl() const { return false; } }; //-------------------------------DigitNode---------------------------------------- diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index babd84a0097c..36d69f18041b 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -1162,7 +1162,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { Node* arg2 = argument(1); const TypeAryPtr* mtype = (ae == StrIntrinsicNode::UU) ? TypeAryPtr::CHARS : TypeAryPtr::BYTES; - set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), mtype, arg1, arg2, ae))); + set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), arg1, arg2, ae))); clear_upper_avx(); return true; @@ -4631,7 +4631,7 @@ bool LibraryCallKit::inline_Class_cast() { } // Not-subtype or the mirror's klass ptr is nullptr (in case it is a primitive). - enum { _bad_type_path = 1, _prim_path = 2, _npe_path = 3, PATH_LIMIT }; + enum { _bad_type_path = 1, _prim_path = 2, PATH_LIMIT }; RegionNode* region = new RegionNode(PATH_LIMIT); record_for_igvn(region); @@ -4653,8 +4653,7 @@ bool LibraryCallKit::inline_Class_cast() { region->init_req(_bad_type_path, bad_type_ctrl); } if (region->in(_prim_path) != top() || - region->in(_bad_type_path) != top() || - region->in(_npe_path) != top()) { + region->in(_bad_type_path) != top()) { // Let Interpreter throw ClassCastException. PreserveJVMState pjvms(this); if (new_cast_failure_map != nullptr) { @@ -5495,11 +5494,67 @@ LibraryCallKit::generate_method_call(vmIntrinsicID method_id, bool is_virtual, b * be virtual (invokevirtual) or bound (invokespecial). For each case we generate * slightly different code. */ +Node* LibraryCallKit::get_hashcode_from_header(Node* header, RegionNode* unset_region) { + // Get the hash value and check to see that it has been properly assigned. + // We depend on hash_mask being at most 32 bits and avoid the use of + // hash_mask_in_place because it could be larger than 32 bits in a 64-bit + // vm: see markWord.hpp. + Node* hash_mask = _gvn.intcon(markWord::hash_mask); + Node* hash_shift = _gvn.intcon(markWord::hash_shift); + Node* hshifted_header = _gvn.transform(new URShiftXNode(header, hash_shift)); + // This hack lets the hash bits live anywhere in the mark object now, as long + // as the shift drops the relevant bits into the low 32 bits. Note that + // Java spec says that HashCode is an int so there's no point in capturing + // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build). + hshifted_header = ConvX2I(hshifted_header); + Node* hash_val = _gvn.transform(new AndINode(hshifted_header, hash_mask)); + + Node* no_hash_val = _gvn.intcon(markWord::no_hash); + Node* chk_assigned = _gvn.transform(new CmpINode( hash_val, no_hash_val)); + Node* test_assigned = _gvn.transform(new BoolNode( chk_assigned, BoolTest::eq)); + + generate_slow_guard(test_assigned, unset_region); + + return hash_val; +} + +/* The overall logic is something like + * + * null_path: + * if receiver is null { + * if static { return 0 } else { null pointer exception } + * } + * + * cache_path: + * if header is not safe to read { goto inline_fast_path } + * hash = read_hash_from_header() + * if hash is empty { goto inline_fast_path } + * return hash + * + * inline_fast_path: + * if not static { goto slow } + * if not value object { goto slow } + * if value klass has no fast path { goto slow } + * if klass header is not safe to read { goto slow } + * k_hash = read_hash_from_klass_header() + * if k_hash is empty { goto slow } + * return fast_hashcode_path (see InlineKlass::Members::_fast_hashcode_offset et seqq. for details on how this is computed) + * + * slow: + * runtime call to hash function (this may be replaced with the expanded form during IGVN. See CallStaticJavaNode::replace_identity_hash_code) + * + */ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { assert(is_static == callee()->is_static(), "correct intrinsic selection"); assert(!(is_virtual && is_static), "either virtual, special, or static"); - enum { _slow_path = 1, _fast_path, _null_path, PATH_LIMIT }; + enum { + _slow_path = 1, // Actually perform the runtime call + _cache_path, // Get the hash from the header + _null_path, // If object is null, hash is 0. + _inline_fast_path, // Fast path for value objects only (see InlineKlass::Members::_fast_hashcode_offset et seqq.) + PATH_LIMIT, + }; RegionNode* result_reg = new RegionNode(PATH_LIMIT); PhiNode* result_val = new PhiNode(result_reg, TypeInt::INT); @@ -5507,10 +5562,11 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { PhiNode* result_mem = new PhiNode(result_reg, Type::MEMORY, TypePtr::BOTTOM); Node* obj = argument(0); - // Don't intrinsify hashcode on inline types for now. - // The "is locked" runtime check also subsumes the inline type check (as inline types cannot be locked) and goes to the slow path. - if (gvn().type(obj)->is_inlinetypeptr()) { - return false; + if (obj->is_InlineType()) { + PreserveReexecuteState preexecs(this); + inc_sp(2); + jvms()->set_should_reexecute(true); + obj = obj->as_InlineType()->buffer(this); } if (!is_static) { @@ -5536,10 +5592,13 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { return true; } - // We only go to the fast case code if we pass a number of guards. The - // paths which do not pass are accumulated in the slow_region. + // We only go to the cache case code if we pass a number of guards. The paths which do + // not pass are accumulated in the inline_fast_path_region. The compute region tries + // to use the fast path for inline types. That also needs a lot of guards to be met. + // The paths which do not pass are accumulated in the slow_region, where we do the + // runtime call, which is the last resort. + RegionNode* inline_fast_path_region = new RegionNode(1); RegionNode* slow_region = new RegionNode(1); - record_for_igvn(slow_region); // If this is a virtual call, we generate a funny guard. We pull out // the vtable entry corresponding to hashCode() from the target object. @@ -5547,9 +5606,9 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { // Object hashCode() method, we pass the guard. We do not need this // guard for non-virtual calls -- the caller is known to be the native // Object hashCode(). + // After null check, get the object's klass. + Node* obj_klass = load_object_klass(obj); if (is_virtual) { - // After null check, get the object's klass. - Node* obj_klass = load_object_klass(obj); generate_virtual_guard(obj_klass, slow_region); } @@ -5560,48 +5619,94 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { Node* no_ctrl = nullptr; Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); - if (!UseObjectMonitorTable) { - // Test the header to see if it is safe to read w.r.t. locking. - // We cannot use the inline type mask as this may check bits that are overridden - // by an object monitor's pointer when inflating locking. - Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place); - Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask)); - Node *monitor_val = _gvn.MakeConX(markWord::monitor_value); - Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val)); - Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq)); + Node* hash_val = get_hashcode_from_header(header, inline_fast_path_region); - generate_slow_guard(test_monitor, slow_region); - } + result_val->init_req(_cache_path, hash_val); + result_reg->init_req(_cache_path, control()); - // Get the hash value and check to see that it has been properly assigned. - // We depend on hash_mask being at most 32 bits and avoid the use of - // hash_mask_in_place because it could be larger than 32 bits in a 64-bit - // vm: see markWord.hpp. - Node *hash_mask = _gvn.intcon(markWord::hash_mask); - Node *hash_shift = _gvn.intcon(markWord::hash_shift); - Node *hshifted_header= _gvn.transform(new URShiftXNode(header, hash_shift)); - // This hack lets the hash bits live anywhere in the mark object now, as long - // as the shift drops the relevant bits into the low 32 bits. Note that - // Java spec says that HashCode is an int so there's no point in capturing - // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build). - hshifted_header = ConvX2I(hshifted_header); - Node *hash_val = _gvn.transform(new AndINode(hshifted_header, hash_mask)); - - Node *no_hash_val = _gvn.intcon(markWord::no_hash); - Node *chk_assigned = _gvn.transform(new CmpINode( hash_val, no_hash_val)); - Node *test_assigned = _gvn.transform(new BoolNode( chk_assigned, BoolTest::eq)); - - generate_slow_guard(test_assigned, slow_region); + set_control(_gvn.transform(inline_fast_path_region)); + IfNode* fast_path_iff = nullptr; + if (!stopped()) { + if (UseHashcodeFastPath && is_static && !_gvn.type(obj)->is_inlinetypeptr()) { + Node* is_not_value = inline_type_test(obj, false); + generate_fair_guard(is_not_value, slow_region); + if (!stopped()) { + // See InlineKlass::Members::_fast_hashcode_offset et seqq. for details on the fast path logic + Node* members_addr = off_heap_plus_addr(obj_klass, in_bytes(InlineKlass::adr_members_offset())); + Node* members = make_load(control(), members_addr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); + Node* offset_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_hashcode_offset_offset())); + Node* offset = make_load(control(), offset_addr, TypeInt::INT, T_INT, MemNode::unordered); + Node* bol_no_fast_path = BoolCmpI(offset, BoolTest::lt, zerocon(T_INT)); + generate_slow_guard(bol_no_fast_path, slow_region); + if (!stopped()) { + if (control()->is_IfFalse()) { + fast_path_iff = control()->in(0)->as_If(); + } + Node* klass_header_addr = off_heap_plus_addr(load_mirror_from_klass(obj_klass), oopDesc::mark_offset_in_bytes()); + Node* klass_header = make_load(no_ctrl, klass_header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); + Node* result_empty = get_hashcode_from_header(klass_header, slow_region); + if (!stopped()) { + // Now that we know fast path applies, there are 3 cases to distinguish here, + // that unmasked_region/unmasked_result merge: + // 1. the object has no segment, the hash is simply the hash of the class object + // 2. the object has one segment of size smaller than 8 (1, 2, 4) + // 3. the object has one segment of size 8 (long-sized) + // See inlineKlass.hpp on why and how to tell them apart. + RegionNode* unmasked_region = new RegionNode(4); + Node* unmasked_result = new PhiNode(unmasked_region, TypeInt::INT); + + // Case 1. no segment + Node* bol_empty_object = BoolCmpI(offset, BoolTest::eq, zerocon(T_INT)); + IfNode* iff_is_empty_object = create_and_map_if(control(), bol_empty_object, PROB_FAIR, COUNT_UNKNOWN); + unmasked_region->init_req(1, IfTrue(iff_is_empty_object)); + unmasked_result->init_req(1, result_empty); + + set_control(IfFalse(iff_is_empty_object)); + + Node* obj_payload_addr = basic_plus_adr(obj, ConvI2L(offset)); + Node* obj_payload = make_load(control(), obj_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true); + + Node* shift_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_hashcode_shift_offset())); + Node* shift = make_load(control(), shift_addr, TypeInt::INT, T_INT, MemNode::unordered); +#ifdef VM_LITTLE_ENDIAN + // *(obj + offset) >> shift + Node* obj_extracted = RShiftL(obj_payload, shift); +#else + // (*(obj + offset) << shift) >> shift + Node* obj_payload_left_shifted = LShiftL(obj_payload, shift); + Node* obj_extracted = RShiftL(obj_payload_left_shifted, shift); +#endif + Node* is_long_payload_bol = BoolCmpI(shift, BoolTest::eq, intcon(0)); + IfNode* iff_is_long_payload = create_and_map_if(control(), is_long_payload_bol, PROB_FAIR, COUNT_UNKNOWN); + + // Case 2. one segment, less than 8-byte long + Node* result_int = AddI(MulI(intcon(31), result_empty), ConvL2I(obj_extracted)); + unmasked_region->init_req(2, IfFalse(iff_is_long_payload)); + unmasked_result->init_req(2, result_int); + + // Case 3. one segment, 8-byte long + Node* result_long = AddI(MulI(intcon(31), result_int), ConvL2I(URShiftL(obj_extracted, intcon(32)))); + unmasked_region->init_req(3, IfTrue(iff_is_long_payload)); + unmasked_result->init_req(3, result_long); + + Node* fast_path_result = AndI(_gvn.transform(unmasked_result), intcon(markWord::hash_mask)); + result_reg->init_req(_inline_fast_path, _gvn.transform(unmasked_region)); + result_val->init_req(_inline_fast_path, fast_path_result); + } + } + } + } else { + slow_region->add_req(control()); + } + } Node* init_mem = reset_memory(); - // fill in the rest of the null path: result_io ->init_req(_null_path, i_o()); result_mem->init_req(_null_path, init_mem); - - result_val->init_req(_fast_path, hash_val); - result_reg->init_req(_fast_path, control()); - result_io ->init_req(_fast_path, i_o()); - result_mem->init_req(_fast_path, init_mem); + result_io ->init_req(_cache_path, i_o()); + result_mem->init_req(_cache_path, init_mem); + result_io ->set_req(_inline_fast_path, i_o()); + result_mem ->set_req(_inline_fast_path, init_mem); // Generate code for the slow case. We make a call to hashCode(). set_control(_gvn.transform(slow_region)); @@ -5610,7 +5715,9 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { set_all_memory(init_mem); vmIntrinsics::ID hashCode_id = is_static ? vmIntrinsics::_identityHashCode : vmIntrinsics::_hashCode; CallJavaNode* slow_call = generate_method_call(hashCode_id, is_virtual, is_static, false); + slow_call->set_req(TypeFunc::Parms, obj); // This obj is not null Node* slow_result = set_results_for_java_call(slow_call); + assert(hashcode_fast_path_if_from_identity_hash_code_call(&_gvn, slow_call) == fast_path_iff, ""); // this->control() comes from set_results_for_java_call result_reg->init_req(_slow_path, control()); result_val->init_req(_slow_path, slow_result); @@ -5625,6 +5732,73 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { set_result(result_reg, result_val); return true; } +IfNode* LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(PhaseGVN* phase, CallJavaNode* call) { + auto is_con_offset = [](Node* node, ByteSize n) -> bool { + if (!node->is_Con()) return false; + TypeNode* con = node->as_Type(); + assert(con->type()->is_intptr_t(), ""); + return con->type()->is_intptr_t()->is_con(in_bytes(n)); + }; + + assert(call->in(TypeFunc::Control) != nullptr, ""); + if (!call->in(TypeFunc::Control)->is_Region()) return nullptr; + RegionNode* region = call->in(TypeFunc::Control)->as_Region(); + for (uint i = 1; i < region->req(); i++) { + assert(region->in(i) != nullptr, ""); + if (!region->in(i)->is_IfProj()) continue; + IfProjNode* if_proj = region->in(i)->as_IfProj(); + if (if_proj->_con != 1) continue; + + assert(if_proj->in(0) != nullptr, ""); + assert(if_proj->in(0)->is_If(), ""); + IfNode* iff = if_proj->in(0)->as_If(); + + assert(iff->in(1) != nullptr, ""); + if (!iff->in(1)->is_Bool()) continue; + BoolNode* lt = iff->in(1)->as_Bool(); + if (lt->_test._test != BoolTest::lt) continue; + + assert(lt->in(1) != nullptr, ""); + if (lt->in(1)->Opcode() != Op_CmpI) continue; + CmpNode* cmp_i = lt->in(1)->as_Cmp(); + + assert(cmp_i->in(1) != nullptr, ""); + assert(cmp_i->in(2) != nullptr, ""); + + if (cmp_i->in(1)->Opcode() != Op_LoadI) continue; + LoadNode* load_offset = cmp_i->in(1)->as_Load(); + if (!cmp_i->in(2)->is_ConI()) continue; + ConINode* zero_i = cmp_i->in(2)->as_ConI(); + assert(zero_i->type()->is_int() != nullptr, ""); + if (!zero_i->type()->is_int()->is_con(0)) continue; + + assert(load_offset->in(2) != nullptr, ""); + if (!load_offset->in(2)->is_AddP()) continue; + AddPNode* offset_addr_add = load_offset->in(2)->as_AddP(); + + assert(offset_addr_add->in(AddPNode::Base) != nullptr, ""); + assert(offset_addr_add->in(AddPNode::Address) != nullptr, ""); + assert(offset_addr_add->in(AddPNode::Offset) != nullptr, ""); + if (!offset_addr_add->in(AddPNode::Base)->is_top()) continue; + if (offset_addr_add->in(AddPNode::Address)->Opcode() != Op_LoadP) continue; + LoadNode* load_members = offset_addr_add->in(AddPNode::Address)->as_Load(); + if (!is_con_offset(offset_addr_add->in(AddPNode::Offset), InlineKlass::fast_hashcode_offset_offset())) continue; + + assert(load_members->in(2) != nullptr, ""); + if (!load_members->in(2)->is_AddP()) continue; + AddPNode* members_addr_add = load_members->in(2)->as_AddP(); + + assert(members_addr_add->in(AddPNode::Base) != nullptr, ""); + assert(members_addr_add->in(AddPNode::Address) != nullptr, ""); + assert(members_addr_add->in(AddPNode::Offset) != nullptr, ""); + if (!members_addr_add->in(AddPNode::Base)->is_top()) continue; + if (!phase->type(members_addr_add->in(AddPNode::Address))->isa_instklassptr()) continue; + if (!is_con_offset(members_addr_add->in(AddPNode::Offset), InlineKlass::adr_members_offset())) continue; + + return iff; + } + return nullptr; +} //---------------------------inline_native_getClass---------------------------- // public final native Class java.lang.Object.getClass(); @@ -7073,14 +7247,11 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { // 'src_start' points to src array + scaled offset // 'dst_start' points to dst array + scaled offset - // See GraphKit::compress_string - const TypePtr* adr_type; - Node* mem = capture_memory(adr_type, src_type, dst_type); - Node* enc = new EncodeISOArrayNode(control(), mem, adr_type, src_start, dst_start, length, ascii); + const TypeAryPtr* mtype = TypeAryPtr::BYTES; + Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length, ascii); enc = _gvn.transform(enc); Node* res_mem = _gvn.transform(new SCMemProjNode(enc)); - memory_effect(res_mem, src_type, dst_type); - + set_memory(res_mem, mtype); set_result(enc); clear_upper_avx(); @@ -7559,8 +7730,7 @@ bool LibraryCallKit::inline_vectorizedHashCode() { // Resolve address of first element Node* array_start = array_element_address(array, offset, bt); - const TypeAryPtr* in_adr_type = TypeAryPtr::get_array_body_type(bt); - set_result(_gvn.transform(new VectorizedHashCodeNode(control(), memory(in_adr_type), in_adr_type, + set_result(_gvn.transform(new VectorizedHashCodeNode(control(), memory(TypeAryPtr::get_array_body_type(bt)), array_start, length, initialValue, basic_type))); clear_upper_avx(); @@ -8628,7 +8798,7 @@ bool LibraryCallKit::inline_kyberNtt() { if (!stubAddr) return false; Node* coeffs = argument(0); - Node* ntt_zetas = argument(1); + Node* ntt_zetas = argument(1); coeffs = must_be_not_null(coeffs, true); ntt_zetas = must_be_not_null(ntt_zetas, true); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 7e3d65060d90..ce9735cc1748 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -312,7 +312,11 @@ class LibraryCallKit : public GraphKit { bool inline_native_clone(bool is_virtual); bool inline_native_Reflection_getCallerClass(); // Helper function for inlining native object hash method + Node* get_hashcode_from_header(Node* header, RegionNode* unset_region); bool inline_native_hashcode(bool is_virtual, bool is_static); +public: + static IfNode* hashcode_fast_path_if_from_identity_hash_code_call(PhaseGVN* phase, CallJavaNode* call); +private: bool inline_native_getClass(); // Helper functions for inlining arraycopy diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 3b824d810871..9c194774421a 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2338,11 +2338,10 @@ void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) { Node* offset_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_acmp_offset_offset())); Node* offset = make_load(control(), offset_addr, TypeInt::INT, T_INT, MemNode::unordered); - Node* offset_cmp = CmpI(offset, zerocon(T_INT)); - Node* offset_bol = _gvn.transform(new BoolNode(offset_cmp, BoolTest::lt)); + Node* offset_bol = BoolCmpI(offset, BoolTest::lt, zerocon(T_INT)); mask_iff = create_and_map_if(control(), offset_bol, PROB_FAIR, COUNT_UNKNOWN); - Node* slow_path_ctl = _gvn.transform(new IfTrueNode(mask_iff)); - Node* fast_path_ctl = _gvn.transform(new IfFalseNode(mask_iff)); + Node* slow_path_ctl = IfTrue(mask_iff); + Node* fast_path_ctl = IfFalse(mask_iff); set_control(slow_path_ctl); { @@ -2356,11 +2355,11 @@ void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) { // *(left + offset) & mask == *(right + offset) & mask Node* left_payload_addr = basic_plus_adr(not_null_left, offset_l); Node* left_payload = make_load(control(), left_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true); - Node* left_masked = _gvn.transform(new AndLNode(left_payload, fast_acmp_mask)); + Node* left_masked = AndL(left_payload, fast_acmp_mask); Node* right_payload_addr = basic_plus_adr(not_null_right, offset_l); Node* right_payload = make_load(control(), right_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true); - Node* right_masked = _gvn.transform(new AndLNode(right_payload, fast_acmp_mask)); + Node* right_masked = AndL(right_payload, fast_acmp_mask); Node* masked_cmp = CmpL(left_masked, right_masked); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 4e140337d708..9ec0af0e1701 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2708,6 +2708,29 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_AndI || u->Opcode() == Op_AndL; }); } + // If changed LShift inputs, check CompressBits and ExpandBits users for + // compress(x, 1 << n), compress(x, -1 << n), + // expand(x, 1 << n), expand(x, -1 << n) optimizations. + if (use_op == Op_LShiftI || use_op == Op_LShiftL) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return (u->Opcode() == Op_CompressBits || u->Opcode() == Op_ExpandBits) && + u->in(2) == use; + }); + } + // If changed ExpandBits inputs, check CompressBits users for + // compress(expand(x, m), m) optimization. + if (use_op == Op_ExpandBits) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == Op_CompressBits && u->in(1) == use; + }); + } + // If changed CompressBits inputs, check ExpandBits users for + // expand(compress(x, m), m) optimization. + if (use_op == Op_CompressBits) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == Op_ExpandBits && u->in(1) == use; + }); + } // If changed AddI/SubI inputs, check CmpU for range check optimization. if (use_op == Op_AddI || use_op == Op_SubI) { add_users_to_worklist_if(worklist, use, [](Node* u) { diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index b5905bdd99e1..62ea21e7fe29 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1538,7 +1538,6 @@ static const TypeFunc* make_kyberAddPoly_2_Type() { return TypeFunc::make(domain, range); } - // Kyber add 3 polynomials function static const TypeFunc* make_kyberAddPoly_3_Type() { int argcnt = 4; @@ -1560,7 +1559,6 @@ static const TypeFunc* make_kyberAddPoly_3_Type() { return TypeFunc::make(domain, range); } - // Kyber XOF output parsing into polynomial coefficients candidates // or decompress(12,...) function static const TypeFunc* make_kyber12To16_Type() { diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index 45a1bfddc81e..e79fa522051a 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -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 @@ -52,6 +52,9 @@ class StringConcat : public ResourceObj { Node_List _control; // List of control nodes that will be deleted Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten // to restart at the initial JVMState. + Unique_Node_List _allowed_compares; // validate_control_flow() needs to know which compare nodes are + // accepted users of call results. In case of stacked concats, + // these need to be persisted across merges for validation. static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat. // The value 256 was derived by measuring @@ -286,6 +289,10 @@ void StringConcat::eliminate_unneeded_control() { StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { StringConcat* result = new StringConcat(_stringopts, _end); + + Unique_Node_List null_check_ifs; + Unique_Node_List skipped_phis; + for (uint x = 0; x < _control.size(); x++) { Node* n = _control.at(x); if (n->is_Call()) { @@ -311,6 +318,17 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { result->append(other->argument(y), other->mode(y)); } arguments_appended += other->num_arguments(); + // Cache elements for later verification. + if (argument(x)->is_Phi()) { + Node* phi = argument(x); + assert(phi->as_Phi()->is_diamond_phi() > 0, "must be a diamond phi (ref. skip_string_null_check)."); + Node* iff = phi->in(0)->in(1)->in(0); + Node* bol = iff->in(1); + Node* cmpp = bol->as_Bool()->in(1); + null_check_ifs.push(iff); + skipped_phis.push(phi); + result->_allowed_compares.push(cmpp); + } } else { result->append(argx, mode(x)); arguments_appended++; @@ -327,13 +345,55 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { return nullptr; } } + + // Verify that the diamond region isn't shared with non-null check phis; + // and that the associated bool doesn't have external uses. + for (uint i = 0; i < skipped_phis.size(); i++) { + Node* n = skipped_phis.at(i); + Node* r = n->in(0); + for (SimpleDUIterator j(r); j.has_next(); j.next()) { + Node* n2 = j.get(); + if (n2->is_Phi() && !n2->is_memory_phi() && !skipped_phis.member(n2)) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("null-check diamond region has external phi uses"); + } +#endif + return nullptr; + } + } + Node* iff = n->in(0)->in(1)->in(0); + Node* bol = iff->in(1); + for (SimpleDUIterator j(bol); j.has_next(); j.next()) { + if (!null_check_ifs.member(j.get())) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("null-check diamond bool has external uses."); + } +#endif + return nullptr; + } + } + } + result->set_allocation(other->_begin); for (uint i = 0; i < _constructors.size(); i++) { result->add_constructor(_constructors.at(i)); } + for (uint i = 0; i < other->_constructors.size(); i++) { result->add_constructor(other->_constructors.at(i)); } + + // We add previous _allowed_compares in case of repeated stacked concatenation. + for (uint i = 0; i < _allowed_compares.size(); i++) { + result->_allowed_compares.push(_allowed_compares.at(i)); + } + + for (uint i = 0; i < other->_allowed_compares.size(); i++) { + result->_allowed_compares.push(other->_allowed_compares.at(i)); + } + result->_multiple = true; return result; } @@ -936,6 +996,10 @@ bool StringConcat::validate_control_flow() { int null_check_count = 0; Unique_Node_List ctrl_path; + // Local version of _allowed_compares that stores allowed comparisons discovered during traversal + // but that we won't persist across merges. + Unique_Node_List local_allowed_compares; + assert(_control.contains(_begin), "missing"); assert(_control.contains(_end), "missing"); @@ -993,11 +1057,31 @@ bool StringConcat::validate_control_flow() { Node* v2 = cmp->in(2); Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con); - // Null check of the return of append which can simply be eliminated + // Either a null check of the return of append which can simply be eliminated, + // or possibly of a toString during stacked concats. if (b->_test._test == BoolTest::ne && v2->bottom_type() == TypePtr::NULL_PTR && v1->is_Proj() && ctrl_path.member(v1->in(0))) { - // null check of the return value of the append + if (!is_SB_toString(v1->in(0))) { + // append type + assert(v1->in(0)->as_CallStaticJava()->method()->name() == ciSymbols::append_name(), "must be"); + local_allowed_compares.push(cmp); + } else { + // toString + assert(_multiple, "if not _multiple, we should not have a toString on this control path"); + if (!_allowed_compares.member(cmp)) { + // Should have been populated during merge if valid. + // This should also be caught in result use verification later but we can fail early here. + fail = true; +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("Failing as toString()-dependent compare is not part of a recognized string null check."); + cmp->dump(); + } +#endif + break; + } + } null_check_count++; if (otherproj->outcnt() == 1) { CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); @@ -1022,6 +1106,8 @@ bool StringConcat::validate_control_flow() { ((v1->is_Proj() && is_SB_toString(v1->in(0)) && ctrl_path.member(v1->in(0))) || (v2->is_Proj() && is_SB_toString(v2->in(0)) && ctrl_path.member(v2->in(0))))) { // iftrue -> if -> bool -> cmpp -> resproj -> tostring + assert(!_allowed_compares.member(cmp) && !local_allowed_compares.member(cmp), "Unsound dependency on intermediate values"); + // Would be caught by containment analysis later but we can fail early here. fail = true; break; } @@ -1147,7 +1233,9 @@ bool StringConcat::validate_control_flow() { continue; } int opc = use->Opcode(); - if (opc == Op_CmpP || opc == Op_Node) { + if (opc == Op_Node || + (opc == Op_CmpP && (use->outcnt() == 1) // The cmpp validation assumes a unique use. + && (local_allowed_compares.member(use) || _allowed_compares.member(use)))) { ctrl_path.push(use); continue; } @@ -1972,9 +2060,9 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { ShouldNotReachHere(); } if (argi > 0) { - // Check that the sum hasn't overflowed + // Check that the sum won't overflow the destination byte array. IfNode* iff = kit.create_and_map_if(kit.control(), - __ Bool(__ CmpI(length, __ intcon(0)), BoolTest::lt), + __ Bool(_gvn->transform(new CmpUNode(length, __ RShiftI(__ intcon(max_jint), coder))), BoolTest::gt), PROB_MIN, COUNT_UNKNOWN); kit.set_control(__ IfFalse(iff)); overflow->set_req(argi, __ IfTrue(iff)); diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index af8d0928b208..003e2d0fbfa4 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -96,7 +96,6 @@ const char* Arguments::_sun_java_launcher = DEFAULT_JAVA_LAUNCHER; bool Arguments::_executing_unit_tests = false; // These parameters are reset in method parse_vm_init_args() -bool Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods; bool Arguments::_UseOnStackReplacement = UseOnStackReplacement; bool Arguments::_BackgroundCompilation = BackgroundCompilation; bool Arguments::_ClipInlining = ClipInlining; @@ -536,7 +535,6 @@ static SpecialFlag const special_jvm_flags[] = { // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, { "InitiatingHeapOccupancyPercent", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, - { "AlwaysCompileLoopMethods", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, // -------------- Obsolete Flags - sorted by expired_in -------------- @@ -547,6 +545,7 @@ static SpecialFlag const special_jvm_flags[] = { #ifdef _LP64 { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, #endif + { "AlwaysCompileLoopMethods", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, @@ -1358,7 +1357,6 @@ void Arguments::set_mode_flags(Mode mode) { // Default values may be platform/compiler dependent - // use the saved values ClipInlining = Arguments::_ClipInlining; - AlwaysCompileLoopMethods = Arguments::_AlwaysCompileLoopMethods; UseOnStackReplacement = Arguments::_UseOnStackReplacement; BackgroundCompilation = Arguments::_BackgroundCompilation; @@ -1370,7 +1368,6 @@ void Arguments::set_mode_flags(Mode mode) { case _int: UseCompiler = false; UseLoopCounter = false; - AlwaysCompileLoopMethods = false; UseOnStackReplacement = false; break; case _mixed: @@ -1676,7 +1673,6 @@ Arguments::ArgsRange Arguments::parse_memory_size(const char* s, jint Arguments::parse_vm_init_args(GrowableArrayCHeap* all_args) { // Save default settings for some mode flags - Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods; Arguments::_UseOnStackReplacement = UseOnStackReplacement; Arguments::_ClipInlining = ClipInlining; Arguments::_BackgroundCompilation = BackgroundCompilation; @@ -3442,17 +3438,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { return JNI_OK; } -void Arguments::set_compact_headers_flags() { -#ifdef _LP64 - if (UseCompactObjectHeaders && !UseObjectMonitorTable) { - if (FLAG_IS_CMDLINE(UseObjectMonitorTable)) { - warning("-UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable"); - } - FLAG_SET_DEFAULT(UseObjectMonitorTable, true); - } -#endif -} - jint Arguments::apply_ergo() { // Set flags based on ergonomics. jint result = set_ergonomics_flags(); @@ -3463,8 +3448,6 @@ jint Arguments::apply_ergo() { GCConfig::arguments()->initialize(); - set_compact_headers_flags(); - CompressedKlassPointers::pre_initialize(); CDSConfig::ergo_initialize(); @@ -3535,6 +3518,7 @@ jint Arguments::apply_ergo() { DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseNullFreeAtomicValueFlattening); DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseNullableNonAtomicValueFlattening); DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseAcmpFastPath); + DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseHashcodeFastPath); DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(PrintInlineLayout); DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(PrintFlatArrayLayout); DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(IgnoreAssertUnsetFields); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 243349a4f0c0..80f993eba70b 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -253,7 +253,6 @@ class Arguments : AllStatic { static bool _has_jdwp_agent; // Used to save default settings - static bool _AlwaysCompileLoopMethods; static bool _UseOnStackReplacement; static bool _BackgroundCompilation; static bool _ClipInlining; @@ -262,7 +261,6 @@ class Arguments : AllStatic { static void set_conservative_max_heap_alignment(); static void set_use_compressed_oops(); static jint set_ergonomics_flags(); - static void set_compact_headers_flags(); // Bytecode rewriting static void set_bytecode_flags(); diff --git a/src/hotspot/share/runtime/basicLock.cpp b/src/hotspot/share/runtime/basicLock.cpp index 4a6e7402dfa5..73f9cc94b0aa 100644 --- a/src/hotspot/share/runtime/basicLock.cpp +++ b/src/hotspot/share/runtime/basicLock.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 @@ -29,11 +29,9 @@ void BasicLock::print_on(outputStream* st, oop owner) const { st->print("monitor"); - if (UseObjectMonitorTable) { - ObjectMonitor* mon = object_monitor_cache(); - if (mon != nullptr) { - mon->print_on(st); - } + ObjectMonitor* mon = object_monitor_cache(); + if (mon != nullptr) { + mon->print_on(st); } } @@ -66,15 +64,8 @@ void BasicLock::move_to(oop obj, BasicLock* dest) { // small (given the support for inflated fast-path locking in the fast_lock, etc) // we'll leave that optimization for another time. - if (UseObjectMonitorTable) { - // Preserve the ObjectMonitor*, the cache is cleared when a box is reused - // and only read while the lock is held, so no stale ObjectMonitor* is - // encountered. - dest->set_object_monitor_cache(object_monitor_cache()); - } -#ifdef ASSERT - else { - dest->set_bad_monitor_deopt(); - } -#endif + // Preserve the ObjectMonitor*, the cache is cleared when a box is reused + // and only read while the lock is held, so no stale ObjectMonitor* is + // encountered. + dest->set_object_monitor_cache(object_monitor_cache()); } diff --git a/src/hotspot/share/runtime/basicLock.inline.hpp b/src/hotspot/share/runtime/basicLock.inline.hpp index 9f0f26ee9570..29c7b65d1905 100644 --- a/src/hotspot/share/runtime/basicLock.inline.hpp +++ b/src/hotspot/share/runtime/basicLock.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ #include "runtime/objectMonitor.inline.hpp" inline ObjectMonitor* BasicLock::object_monitor_cache() const { - assert(UseObjectMonitorTable, "must be"); #if !defined(ZERO) && (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390)) return reinterpret_cast(get_monitor()); #else @@ -42,12 +41,10 @@ inline ObjectMonitor* BasicLock::object_monitor_cache() const { } inline void BasicLock::clear_object_monitor_cache() { - assert(UseObjectMonitorTable, "must be"); set_monitor(nullptr); } inline void BasicLock::set_object_monitor_cache(ObjectMonitor* mon) { - assert(UseObjectMonitorTable, "must be"); set_monitor(mon); } diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 15d994d02b7e..4a8c1f79a64e 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1280,23 +1280,13 @@ freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller, int real_frame_size = 0; bool augmented = f.was_augmented_on_entry(real_frame_size); if (augmented) { + assert(f.cb()->as_nmethod()->is_compiled_by_c2(), "should be c2 compiled"); // The args reside inside the frame so clear argsize. If the caller is compiled, // this will cause the stack arguments passed by the caller to be freezed when // freezing the caller frame itself. If the caller is interpreted this will have // the effect of discarding the arg area created in the i2c stub. argsize = 0; fsize = real_frame_size - (callee_interpreted ? 0 : callee_argsize); -#ifdef ASSERT - nmethod* nm = f.cb()->as_nmethod(); - Method* method = nm->method(); - address return_pc = ContinuationHelper::CompiledFrame::return_pc(f); - CodeBlob* caller_cb = CodeCache::find_blob_fast(return_pc); - assert(nm->is_compiled_by_c2(), "caller should be c2 compiled"); - assert((!caller_cb->is_nmethod() && nm->is_compiled_by_c2()) || - (nm->compiler_type() != caller_cb->as_nmethod()->compiler_type()) || - (nm->is_compiled_by_c2() && !method->is_static() && method->method_holder()->is_inline_klass()), - "frame should not be extended"); -#endif } log_develop_trace(continuations)("recurse_freeze_compiled_frame %s _size: %d fsize: %d argsize: %d augmented: %d", @@ -1747,6 +1737,8 @@ static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kin Method* m; const char* code_name; int bci; + ResourceMark rm; + if (preempt_kind == Continuation::monitorenter) { assert(top.is_interpreted_frame() || top.is_runtime_frame(), "unexpected %sframe", top.is_compiled_frame() ? "compiled " : top.is_native_frame() ? "native " : ""); @@ -1763,7 +1755,6 @@ static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kin bci = at_sync_method ? -1 : top.interpreter_frame_bci(); } else { JavaThread* current = JavaThread::current(); - ResourceMark rm(current); CodeBlob* cb = top.cb(); RegisterMap reg_map(current, RegisterMap::UpdateMap::skip, @@ -2100,6 +2091,7 @@ class ThawBase : public StackObj { template int remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize); int remove_scalarized_frames(StackChunkFrameStream& scfs, int &argsize); + void check_top_for_deoptimization(stackChunkOop chunk); void copy_from_chunk(intptr_t* from, intptr_t* to, int size); void thaw_lockstack(stackChunkOop chunk); @@ -2212,6 +2204,23 @@ inline void ThawBase::clear_chunk(stackChunkOop chunk) { chunk->set_max_thawing_size(0); } +void ThawBase::check_top_for_deoptimization(stackChunkOop chunk) { + StackChunkFrameStream f(chunk); + if (f.is_stub()) { + f.next(SmallRegisterMap::instance_no_args(), true /* stop */); + assert(!f.is_done(), ""); + + f.get_cb(); + assert(f.is_compiled(), ""); + if (f.cb()->as_nmethod()->is_marked_for_deoptimization()) { + // The caller of the runtime stub when the continuation is preempted is not at a + // Java call instruction, and so cannot rely on nmethod patching for deopt. + log_develop_trace(continuations)("Deoptimizing runtime stub caller"); + f.to_frame().deoptimize(nullptr); // the null thread simply avoids the assertion in deoptimize which we're not set up for + } + } +} + int ThawBase::remove_scalarized_frames(StackChunkFrameStream& f, int &argsize) { intptr_t* top = f.sp(); @@ -2349,6 +2358,9 @@ NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) { if (LIKELY(!ForceSingleFrameThaw && (full_chunk_size < threshold))) { prefetch_chunk_pd(chunk->start_address(), full_chunk_size); // prefetch anticipating memcpy starting at highest address + if (check_stub) { + check_top_for_deoptimization(chunk); + } partial = false; argsize = chunk->argsize(); // must be called *before* clearing the chunk clear_chunk(chunk); @@ -2642,14 +2654,14 @@ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) { if (bottom) { ContinuationHelper::Frame::patch_pc(caller, _cont.is_empty() ? caller.pc() : StubRoutines::cont_returnBarrier()); - } else if (_should_patch_caller_pc || caller.is_compiled_frame()) { + } else if (_should_patch_caller_pc) { // Caller was deoptimized during thaw but we've overwritten the return address when copying f from the heap. // Also, on some platforms, if the caller is interpreted but the callee not we also need to patch. #if defined(PPC64) || defined(S390) - assert(!_should_patch_caller_pc || caller.is_deoptimized_frame() || caller.is_interpreted_frame(), ""); + assert(caller.is_deoptimized_frame() || caller.is_interpreted_frame(), ""); #else - assert(!_should_patch_caller_pc || caller.is_deoptimized_frame(), ""); + assert(caller.is_deoptimized_frame(), ""); #endif ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc()); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 1364133d8ecf..bcf86d0f6a37 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1481,15 +1481,7 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArraycurrent_waiting_monitor(); if (waiting_monitor != nullptr && waiting_monitor->object() == obj()) { assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization"); - if (UseObjectMonitorTable) { - mon_info->lock()->clear_object_monitor_cache(); - } -#ifdef ASSERT - else { - assert(!UseObjectMonitorTable, "must be"); - mon_info->lock()->set_bad_monitor_deopt(); - } -#endif + mon_info->lock()->clear_object_monitor_cache(); JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread); continue; } @@ -1499,12 +1491,9 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArrayclear_object_monitor_cache(); - } + // The BasicLock cache is expected to be either a valid ObjectMonitor* + // or nullptr. Right now it is garbage, hence we set it to nullptr. + lock->clear_object_monitor_cache(); ObjectSynchronizer::enter_for(obj, lock, deoptee_thread); if (deoptee_thread->lock_stack().contains(obj())) { ObjectSynchronizer::inflate_fast_locked_object(obj(), ObjectSynchronizer::InflateCause::inflate_cause_vm_internal, diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 26756ea00226..edfcf2e53629 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1224,10 +1224,6 @@ const int ObjectAlignmentInBytes = 8; product(bool, UseCompiler, true, \ "Use Just-In-Time compilation") \ \ - product(bool, AlwaysCompileLoopMethods, false, \ - "(Deprecated) When using recompilation, never interpret methods " \ - "containing loops") \ - \ product(int, AllocatePrefetchStyle, 1, \ "0 = no prefetch, " \ "1 = generate prefetch instructions for each allocation, " \ @@ -1985,10 +1981,6 @@ const int ObjectAlignmentInBytes = 8; "Mark all threads after a safepoint, and clear on a modify " \ "fence. Add cleanliness checks.") \ \ - product(bool, UseObjectMonitorTable, true, DIAGNOSTIC, \ - "Use a table to record inflated monitors rather than the first " \ - "word of the object.") \ - \ product(int, FastLockingSpins, 8, DIAGNOSTIC, \ "Specifies the number of times fast locking will attempt to " \ "CAS the markWord before inflating. Between each CAS it will " \ @@ -2037,6 +2029,9 @@ const int ObjectAlignmentInBytes = 8; \ product(bool, UseAcmpFastPath, true, DIAGNOSTIC, \ "Use fast path for acmp.") \ + \ + product(bool, UseHashcodeFastPath, true, DIAGNOSTIC, \ + "Use fast path for identityHashCode.") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/interfaceSupport.cpp b/src/hotspot/share/runtime/interfaceSupport.cpp index 6ccf63b4c5e5..2b8d87cbf573 100644 --- a/src/hotspot/share/runtime/interfaceSupport.cpp +++ b/src/hotspot/share/runtime/interfaceSupport.cpp @@ -85,10 +85,17 @@ unsigned int InterfaceSupport::_fullgc_alot_counter = 1; intx InterfaceSupport::_fullgc_alot_invocation = 0; void InterfaceSupport::gc_alot() { - Thread *thread = Thread::current(); + Thread* thread = Thread::current(); if (!thread->is_Java_thread()) return; // Avoid concurrent calls + JavaThread* current_thread = JavaThread::cast(thread); + // Do not request a GC in a critical section: garbage collectors in this state + // cannot complete a GC until all threads have left the JNI critical sections, + // and threads cannot leave while they are waiting for GC, thus deadlocking. + if (current_thread->in_critical()) return; + // A GC would try to acquire Heap_lock in the prologue. Do not try to acquire the + // lock recursively as this would cause a hang. + if (Heap_lock->owned_by_self()) return; // Check for new, not quite initialized thread. A thread in new mode cannot initiate a GC. - JavaThread *current_thread = JavaThread::cast(thread); if (current_thread->active_handles() == nullptr) return; // Short-circuit any possible re-entrant gc-a-lot attempt diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index c0877c470367..8e5fde069c68 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -281,7 +281,7 @@ void JavaThread::check_possible_safepoint() { #endif // CHECK_UNHANDLED_OOPS } -void JavaThread::check_for_valid_safepoint_state() { +void JavaThread::check_for_valid_safepoint_state(bool allow_gcalot) { // Don't complain if running a debugging command. if (DebuggingContext::is_enabled()) return; @@ -294,7 +294,7 @@ void JavaThread::check_for_valid_safepoint_state() { fatal("LEAF method calling lock?"); } - if (GCALotAtAllSafepoints) { + if (GCALotAtAllSafepoints && allow_gcalot) { // We could enter a safepoint here and thus have a gc InterfaceSupport::check_gc_alot(); } diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 698f64dd97ed..b08a4e6da007 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -284,8 +284,8 @@ class JavaThread: public Thread { public: // These functions check conditions before possibly going to a safepoint. // including NoSafepointVerifier. - void check_for_valid_safepoint_state() NOT_DEBUG_RETURN; - void check_possible_safepoint() NOT_DEBUG_RETURN; + void check_for_valid_safepoint_state(bool allow_gcalot = true) NOT_DEBUG_RETURN; + void check_possible_safepoint() NOT_DEBUG_RETURN; #ifdef ASSERT private: diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index 5b4556b56668..a5a3f9990cfb 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -258,7 +258,6 @@ inline InstanceKlass* JavaThread::class_being_initialized() const { } inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) { - assert(UseObjectMonitorTable, "must be"); assert(monitor != nullptr, "use om_clear_monitor_cache to clear"); assert(this == current() || monitor->has_owner(this), "only add owned monitors for other threads"); assert(this == current() || is_obj_deopt_suspend(), "thread must not run concurrently"); @@ -267,9 +266,7 @@ inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) { } inline void JavaThread::om_clear_monitor_cache() { - if (UseObjectMonitorTable) { - _om_cache.clear(); - } + _om_cache.clear(); } inline ObjectMonitor* JavaThread::om_get_from_monitor_cache(oop obj) { diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp index 8e0c1d10e5c6..9f3be83b3e0b 100644 --- a/src/hotspot/share/runtime/mutex.cpp +++ b/src/hotspot/share/runtime/mutex.cpp @@ -61,7 +61,7 @@ void Mutex::check_block_state(Thread* thread) { "locking not allowed when crash protection is set"); } -void Mutex::check_safepoint_state(Thread* thread) { +void Mutex::check_safepoint_state(Thread* thread, bool allow_gcalot) { check_block_state(thread); // If the lock acquisition checks for safepoint, verify that the lock was created with rank that @@ -72,7 +72,7 @@ void Mutex::check_safepoint_state(Thread* thread) { if (thread->is_active_Java_thread()) { // Also check NoSafepointVerifier, and thread state is _thread_in_vm - JavaThread::cast(thread)->check_for_valid_safepoint_state(); + JavaThread::cast(thread)->check_for_valid_safepoint_state(allow_gcalot); } } @@ -116,7 +116,7 @@ void Mutex::lock_contended(Thread* self) { void Mutex::lock(Thread* self) { assert(owner() != self, "invariant"); - check_safepoint_state(self); + check_safepoint_state(self, true /* allow_gcalot */); check_rank(self); OrderAccess::fence(); @@ -245,7 +245,11 @@ bool Monitor::wait(uint64_t timeout) { set_owner(nullptr); // Check safepoint state after resetting owner and possible NSV. - check_safepoint_state(self); + // Although the (HotSpot) monitor is logically released, the underlying + // OS monitor is still held. If this is the Heap_lock we would + // deadlock in the GC prologue trying to acquire the lock recursively. + // Suppress GC-a-lot in that case. + check_safepoint_state(self, this != Heap_lock); int wait_status; InFlightMutexRelease ifmr(this); diff --git a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp index 4d30a320cbf8..e497fbb34585 100644 --- a/src/hotspot/share/runtime/mutex.hpp +++ b/src/hotspot/share/runtime/mutex.hpp @@ -141,7 +141,7 @@ class Mutex : public CHeapObj { protected: void set_owner_implementation(Thread* owner) NOT_DEBUG({ raw_set_owner(owner);}); void check_block_state (Thread* thread) NOT_DEBUG_RETURN; - void check_safepoint_state (Thread* thread) NOT_DEBUG_RETURN; + void check_safepoint_state (Thread* thread, bool allow_gcalot) NOT_DEBUG_RETURN; void check_no_safepoint_state(Thread* thread) NOT_DEBUG_RETURN; void check_rank (Thread* thread) NOT_DEBUG_RETURN; void assert_owner (Thread* expected) NOT_DEBUG_RETURN; diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 22cc0c848910..ee74afcaf1ff 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -344,25 +344,11 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) { } } -#define assert_mark_word_consistency() \ - assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \ - "object mark must match encoded this: mark=" INTPTR_FORMAT \ - ", encoded this=" INTPTR_FORMAT, object()->mark().value(), \ - markWord::encode(this).value()); - // ----------------------------------------------------------------------------- // Enter support bool ObjectMonitor::enter_is_async_deflating() { if (is_being_async_deflated()) { - if (!UseObjectMonitorTable) { - const oop l_object = object(); - if (l_object != nullptr) { - // Attempt to restore the header/dmw to the object's header so that - // we only retry once if the deflater thread happens to be slow. - install_displaced_markword_in_object(l_object); - } - } return true; } @@ -489,7 +475,6 @@ bool ObjectMonitor::spin_enter(JavaThread* current) { if (try_spin(current)) { assert(has_owner(current), "must be current: owner=" INT64_FORMAT, owner_raw()); assert(_recursions == 0, "must be 0: recursions=%zd", _recursions); - assert_mark_word_consistency(); return true; } @@ -629,7 +614,6 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito assert(_recursions == 0, "invariant"); assert(has_owner(current), "invariant"); assert(!has_successor(current), "invariant"); - assert_mark_word_consistency(); // The thread -- now the owner -- is back in vm mode. // Report the glorious news via TI,DTrace and jvmstat. @@ -856,12 +840,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { } } - if (UseObjectMonitorTable) { - ObjectSynchronizer::deflate_monitor(obj, this); - } else if (obj != nullptr) { - // Install the old mark word if nobody else has already done it. - install_displaced_markword_in_object(obj); - } + ObjectSynchronizer::deflate_monitor(obj, this); if (event.should_commit()) { post_monitor_deflate_event(&event, obj); @@ -872,60 +851,6 @@ bool ObjectMonitor::deflate_monitor(Thread* current) { return true; // Success, ObjectMonitor has been deflated. } -// Install the displaced mark word (dmw) of a deflating ObjectMonitor -// into the header of the object associated with the monitor. This -// idempotent method is called by a thread that is deflating a -// monitor and by other threads that have detected a race with the -// deflation process. -void ObjectMonitor::install_displaced_markword_in_object(const oop obj) { - assert(!UseObjectMonitorTable, "ObjectMonitorTable has no dmw"); - // This function must only be called when (owner == DEFLATER_MARKER - // && contentions <= 0), but we can't guarantee that here because - // those values could change when the ObjectMonitor gets moved from - // the global free list to a per-thread free list. - - guarantee(obj != nullptr, "must be non-null"); - - // Separate loads in is_being_async_deflated(), which is almost always - // called before this function, from the load of dmw/header below. - - // _contentions and dmw/header may get written by different threads. - // Make sure to observe them in the same order when having several observers. - OrderAccess::loadload_for_IRIW(); - - const oop l_object = object_peek(); - if (l_object == nullptr) { - // ObjectMonitor's object ref has already been cleared by async - // deflation or GC so we're done here. - return; - } - assert(l_object == obj, "object=" INTPTR_FORMAT " must equal obj=" - INTPTR_FORMAT, p2i(l_object), p2i(obj)); - - markWord dmw = header(); - // The dmw has to be neutral (not null, not locked and not marked). - assert(dmw.is_neutral(), "must be neutral: dmw=" INTPTR_FORMAT, dmw.value()); - - // Install displaced mark word if the object's header still points - // to this ObjectMonitor. More than one racing caller to this function - // can rarely reach this point, but only one can win. - markWord res = obj->cas_set_mark(dmw, markWord::encode(this)); - if (res != markWord::encode(this)) { - // This should be rare so log at the Info level when it happens. - log_info(monitorinflation)("install_displaced_markword_in_object: " - "failed cas_set_mark: new_mark=" INTPTR_FORMAT - ", old_mark=" INTPTR_FORMAT ", res=" INTPTR_FORMAT, - dmw.value(), markWord::encode(this).value(), - res.value()); - } - - // Note: It does not matter which thread restored the header/dmw - // into the object's header. The thread deflating the monitor just - // wanted the object's header restored and it is. The threads that - // detected a race with the deflation process also wanted the - // object's header restored before they retry their operation and - // because it is restored they will only retry once. -} // Convert the fields used by is_busy() to a string that can be // used for diagnostic output. @@ -1977,7 +1902,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // Verify a few postconditions assert(has_owner(current), "invariant"); assert(!has_successor(current), "invariant"); - assert_mark_word_consistency(); if (ce != nullptr && ce->is_virtual_thread()) { current->post_vthread_pinned_event(&vthread_pinned_event, "Object.wait", result); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 848eae6df2ac..3c126a034484 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -154,13 +154,9 @@ class ObjectMonitor : public CHeapObj { // ParkEvent of unblocker thread. static ParkEvent* _vthread_unparker_ParkEvent; - // Because of frequent access, the metadata field is at offset zero (0). - // Enforced by the assert() in metadata_addr(). - // * Locking with UseObjectMonitorTable: - // Contains the _object's hashCode. - // * Locking without UseObjectMonitorTable: - // Contains the displaced object header word - mark - volatile uintptr_t _metadata; // metadata + // Because of frequent access, the _metadata field is at offset zero (0), + // which is enforced by a STATIC_ASSERT() in metadata_addr(). + volatile uintptr_t _metadata; // contains the _object's hashCode WeakHandle _object; // backward object pointer // Separate _metadata and _owner on different cache lines since both can // have busy multi-threaded access. _metadata and _object are set at initial @@ -415,7 +411,6 @@ class ObjectMonitor : public CHeapObj { public: // Deflation support bool deflate_monitor(Thread* current); - void install_displaced_markword_in_object(const oop obj); // JFR support static bool is_jfr_excluded(const Klass* monitor_klass); diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp index efdc33cd4412..5e9d3dee5624 100644 --- a/src/hotspot/share/runtime/objectMonitor.inline.hpp +++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ #include "runtime/synchronizer.hpp" #include "runtime/threadIdentifier.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" inline int64_t ObjectMonitor::owner_id_from(JavaThread* thread) { @@ -74,22 +75,22 @@ inline volatile uintptr_t* ObjectMonitor::metadata_addr() { } inline markWord ObjectMonitor::header() const { - assert(!UseObjectMonitorTable, "Locking with OM table does not use header"); + // Locking with OM table does not use header. + ShouldNotCallThis(); return markWord(metadata()); } inline void ObjectMonitor::set_header(markWord hdr) { - assert(!UseObjectMonitorTable, "Locking with OM table does not use header"); + // Locking with OM table does not use header. + ShouldNotCallThis(); set_metadata(hdr.value()); } inline intptr_t ObjectMonitor::hash() const { - assert(UseObjectMonitorTable, "Only used when locking with OM table"); return metadata(); } inline void ObjectMonitor::set_hash(intptr_t hash) { - assert(UseObjectMonitorTable, "Only used when locking with OM table"); set_metadata(hash); } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index ac487b9f1d1c..aaf9c956ca07 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -592,7 +592,14 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr // native nmethods don't have exception handlers assert(!nm->is_native_method() || nm->method()->is_continuation_enter_intrinsic(), "no exception handler"); assert(nm->header_begin() != nm->exception_begin(), "no exception handler"); - if (nm->is_deopt_pc(return_address)) { + // For platform threads, checking the return pc already covers the case + // where only this compiled frame was deoptimized, as well as the case + // where the nmethod was marked for deoptimization. For virtual threads, + // we also need to check if the nmethod is marked for deoptimization because + // the return pc may not have been patched if the nmethod was deoptimized + // while the frame was frozen. Since this check is benign for platform + // threads, we do it unconditionally. + if (nm->is_deopt_pc(return_address) || nm->is_marked_for_deoptimization()) { // If we come here because of a stack overflow, the stack may be // unguarded. Reguard the stack otherwise if we return to the // deopt blob and the stack bang causes a stack overflow we @@ -3865,15 +3872,7 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) ) kptr2 = fr.next_monitor_in_interpreter_frame(kptr2) ) { if (kptr2->obj() != nullptr) { // Avoid 'holes' in the monitor array BasicLock *lock = kptr2->lock(); - if (UseObjectMonitorTable) { - buf[i] = (intptr_t)lock->object_monitor_cache(); - } -#ifdef ASSERT - else { - buf[i] = badDispHeaderOSR; - } -#endif - i++; + buf[i++] = (intptr_t)lock->object_monitor_cache(); buf[i++] = cast_from_oop(kptr2->obj()); } } diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp index b799e424178b..bfdf39b1c3b5 100644 --- a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp +++ b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -178,8 +178,16 @@ inline bool StackChunkFrameStream::is_interpreted() c // template inline int StackChunkFrameStream::frame_size() const { - return is_interpreted() ? interpreter_frame_size() - : cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top; + if (is_interpreted()) { + return interpreter_frame_size(); + } else if (is_compiled() && cb()->as_nmethod()->needs_stack_repair()) { + int real_frame_size = 0; + frame f = to_frame(); + if (f.was_augmented_on_entry(real_frame_size)) { + return real_frame_size; + } + } + return cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top; } template diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index e5b70ebe426d..fb2f8bd43033 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -680,98 +680,22 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) { assert(!obj->klass()->is_inline_klass(), "FastHashCode should not be called for inline classes"); while (true) { - ObjectMonitor* monitor = nullptr; markWord temp, test; intptr_t hash; markWord mark = obj->mark_acquire(); - // If UseObjectMonitorTable is set the hash can simply be installed in the - // object header, since the monitor isn't in the object header. - if (UseObjectMonitorTable || !mark.has_monitor()) { - hash = mark.hash(); - if (hash != 0) { // if it has a hash, just return it - return hash; - } - hash = get_next_hash(current, obj); // get a new hash - temp = mark.copy_set_hash(hash); // merge the hash into header - // try to install the hash - test = obj->cas_set_mark(temp, mark); - if (test == mark) { // if the hash was installed, return it - return hash; - } - // CAS failed, retry - continue; - - // Failed to install the hash. It could be that another thread - // installed the hash just before our attempt or inflation has - // occurred or... so we fall thru to inflate the monitor for - // stability and then install the hash. - } else { - assert(!mark.is_unlocked() && !mark.is_fast_locked(), "invariant"); - monitor = mark.monitor(); - temp = monitor->header(); - assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value()); - hash = temp.hash(); - if (hash != 0) { - // It has a hash. - - // Separate load of dmw/header above from the loads in - // is_being_async_deflated(). - - // dmw/header and _contentions may get written by different threads. - // Make sure to observe them in the same order when having several observers. - OrderAccess::loadload_for_IRIW(); - - if (monitor->is_being_async_deflated()) { - // But we can't safely use the hash if we detect that async - // deflation has occurred. So we attempt to restore the - // header/dmw to the object's header so that we only retry - // once if the deflater thread happens to be slow. - monitor->install_displaced_markword_in_object(obj); - continue; - } - return hash; - } - // Fall thru so we only have one place that installs the hash in - // the ObjectMonitor. - } - - // NOTE: an async deflation can race after we get the monitor and - // before we can update the ObjectMonitor's header with the hash - // value below. - assert(mark.has_monitor(), "must be"); - monitor = mark.monitor(); - - // Load ObjectMonitor's header/dmw field and see if it has a hash. - mark = monitor->header(); - assert(mark.is_neutral(), "invariant: header=" INTPTR_FORMAT, mark.value()); + // The hash is located in the object header. hash = mark.hash(); - if (hash == 0) { // if it does not have a hash - hash = get_next_hash(current, obj); // get a new hash - temp = mark.copy_set_hash(hash) ; // merge the hash into header - assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value()); - uintptr_t v = AtomicAccess::cmpxchg(monitor->metadata_addr(), mark.value(), temp.value()); - test = markWord(v); - if (test != mark) { - // The attempt to update the ObjectMonitor's header/dmw field - // did not work. This can happen if another thread managed to - // merge in the hash just before our cmpxchg(). - // If we add any new usages of the header/dmw field, this code - // will need to be updated. - hash = test.hash(); - assert(test.is_neutral(), "invariant: header=" INTPTR_FORMAT, test.value()); - assert(hash != 0, "should only have lost the race to a thread that set a non-zero hash"); - } - if (monitor->is_being_async_deflated() && !UseObjectMonitorTable) { - // If we detect that async deflation has occurred, then we - // attempt to restore the header/dmw to the object's header - // so that we only retry once if the deflater thread happens - // to be slow. - monitor->install_displaced_markword_in_object(obj); - continue; - } + if (hash != 0) { // if it has a hash, just return it + return hash; } - // We finally get the hash. - return hash; + hash = get_next_hash(current, obj); // get a new hash + temp = mark.copy_set_hash(hash); // merge the hash into header + // try to install the hash + test = obj->cas_set_mark(temp, mark); + if (test == mark) { // if the hash was installed, return it + return hash; + } + // CAS failed, retry } } @@ -1237,9 +1161,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { unlinked_count = _in_use_list.unlink_deflated(deflated_count, &delete_list, &safepointer); GrowableArray table_delete_list; - if (UseObjectMonitorTable) { - ObjectMonitorTable::rebuild(&table_delete_list); - } + ObjectMonitorTable::rebuild(&table_delete_list); log.before_handshake(unlinked_count); @@ -1260,9 +1182,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { // Delete the unlinked ObjectMonitors. deleted_count = delete_monitors(&delete_list, &safepointer); - if (UseObjectMonitorTable) { - ObjectMonitorTable::destroy(&table_delete_list); - } + ObjectMonitorTable::destroy(&table_delete_list); assert(unlinked_count == deleted_count, "must be"); } @@ -1457,19 +1377,6 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, } const markWord mark = obj->mark(); - // Note: When using ObjectMonitorTable we may observe an intermediate state, - // where the monitor is globally visible, but no thread has yet transitioned - // the markWord. To avoid reporting a false positive during this transition, we - // skip the `!mark.has_monitor()` test if we are using the ObjectMonitorTable. - if (!UseObjectMonitorTable && !mark.has_monitor()) { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " - "object does not think it has a monitor: obj=" - INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n), - p2i(obj), mark.value()); - *error_cnt_p = *error_cnt_p + 1; - return; - } - ObjectMonitor* const obj_mon = read_monitor(obj, mark); if (n != obj_mon) { out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " @@ -1499,7 +1406,7 @@ void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_ monitors_iterate([&](ObjectMonitor* monitor) { if (is_interesting(monitor)) { const oop obj = monitor->object_peek(); - const intptr_t hash = UseObjectMonitorTable ? monitor->hash() : monitor->header().hash(); + const intptr_t hash = monitor->hash(); ResourceMark rm; out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(monitor), monitor->is_busy(), hash != 0, monitor->has_owner(), @@ -1562,8 +1469,6 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event, } ObjectMonitor* ObjectSynchronizer::get_or_insert_monitor(oop object, JavaThread* current, ObjectSynchronizer::InflateCause cause) { - assert(UseObjectMonitorTable, "must be"); - EventJavaMonitorInflate event; bool inserted; @@ -1584,7 +1489,6 @@ ObjectMonitor* ObjectSynchronizer::get_or_insert_monitor(oop object, JavaThread* // Add the hashcode to the monitor to match the object and put it in the hashtable. ObjectMonitor* ObjectSynchronizer::add_monitor(ObjectMonitor* monitor, oop obj) { - assert(UseObjectMonitorTable, "must be"); assert(obj == monitor->object(), "must be"); intptr_t hash = obj->mark().hash(); @@ -1595,15 +1499,12 @@ ObjectMonitor* ObjectSynchronizer::add_monitor(ObjectMonitor* monitor, oop obj) } void ObjectSynchronizer::remove_monitor(ObjectMonitor* monitor, oop obj) { - assert(UseObjectMonitorTable, "must be"); assert(monitor->object_peek() == obj, "must be, cleared objects are removed by is_dead"); ObjectMonitorTable::remove_monitor_entry(monitor); } void ObjectSynchronizer::deflate_mark_word(oop obj) { - assert(UseObjectMonitorTable, "must be"); - markWord mark = obj->mark_acquire(); assert(!mark.has_no_hash(), "obj with inflated monitor must have had a hash"); @@ -1614,9 +1515,6 @@ void ObjectSynchronizer::deflate_mark_word(oop obj) { } void ObjectSynchronizer::create_om_table() { - if (!UseObjectMonitorTable) { - return; - } ObjectMonitorTable::create(); } @@ -1684,18 +1582,15 @@ class ObjectSynchronizer::CacheSetter : StackObj { _monitor(nullptr) {} ~CacheSetter() { - // Only use the cache if using the table. - if (UseObjectMonitorTable) { - if (_monitor != nullptr) { - // If the monitor is already in the BasicLock cache then it is most - // likely in the thread cache, do not set it again to avoid reordering. - if (_monitor != _lock->object_monitor_cache()) { - _thread->om_set_monitor_cache(_monitor); - _lock->set_object_monitor_cache(_monitor); - } - } else { - _lock->clear_object_monitor_cache(); + if (_monitor != nullptr) { + // If the monitor is already in the BasicLock cache then it is most + // likely in the thread cache, do not set it again to avoid reordering. + if (_monitor != _lock->object_monitor_cache()) { + _thread->om_set_monitor_cache(_monitor); + _lock->set_object_monitor_cache(_monitor); } + } else { + _lock->clear_object_monitor_cache(); } } @@ -1754,7 +1649,6 @@ inline bool ObjectSynchronizer::fast_lock_try_enter(oop obj, LockStack& lock_sta } bool ObjectSynchronizer::fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation) { - assert(UseObjectMonitorTable, "must be"); // Will spin with exponential backoff with an accumulative O(2^spin_limit) spins. const int log_spin_limit = os::is_MP() ? FastLockingSpins : 1; const int log_min_safepoint_check_interval = 10; @@ -1801,7 +1695,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock // deoptimizing and re-locking locks. See Deoptimization::relock_objects assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be"); - assert(!UseObjectMonitorTable || lock->object_monitor_cache() == nullptr, "must be cleared"); + assert(lock->object_monitor_cache() == nullptr, "must be cleared"); JavaThread* current = JavaThread::current(); VerifyThreadState vts(locking_thread, current); @@ -1825,7 +1719,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock } assert(monitor != nullptr, "ObjectSynchronizer::enter_for must succeed"); - assert(!UseObjectMonitorTable || lock->object_monitor_cache() == nullptr, "unused. already cleared"); + assert(lock->object_monitor_cache() == nullptr, "unused. already cleared"); } void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) { @@ -1866,7 +1760,7 @@ void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) // If deflation has been observed we also spin while deflation is ongoing. if (fast_lock_try_enter(obj(), lock_stack, current)) { return; - } else if (UseObjectMonitorTable && fast_lock_spin_enter(obj(), lock_stack, current, observed_deflation)) { + } else if (fast_lock_spin_enter(obj(), lock_stack, current, observed_deflation)) { return; } @@ -1923,13 +1817,9 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) assert(mark.has_monitor(), "must be"); // The monitor exists ObjectMonitor* monitor; - if (UseObjectMonitorTable) { - monitor = read_caches(current, lock, object); - if (monitor == nullptr) { - monitor = get_monitor_from_table(object); - } - } else { - monitor = ObjectSynchronizer::read_monitor(mark); + monitor = read_caches(current, lock, object); + if (monitor == nullptr) { + monitor = get_monitor_from_table(object); } if (monitor->has_anonymous_owner()) { assert(current->lock_stack().contains(object), "current must have object on its lock stack"); @@ -1990,131 +1880,12 @@ ObjectMonitor* ObjectSynchronizer::inflate_locked_or_imse(oop obj, ObjectSynchro } } -ObjectMonitor* ObjectSynchronizer::inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current) { - - // The JavaThread* locking parameter requires that the locking_thread == JavaThread::current, - // or is suspended throughout the call by some other mechanism. - // Even with fast locking the thread might be nullptr when called from a non - // JavaThread. (As may still be the case from FastHashCode). However it is only - // important for the correctness of the fast locking algorithm that the thread - // is set when called from ObjectSynchronizer::enter from the owning thread, - // ObjectSynchronizer::enter_for from any thread, or ObjectSynchronizer::exit. - EventJavaMonitorInflate event; - - for (;;) { - const markWord mark = object->mark_acquire(); - - // The mark can be in one of the following states: - // * inflated - If the ObjectMonitor owner is anonymous and the - // locking_thread owns the object lock, then we make the - // locking_thread the ObjectMonitor owner and remove the - // lock from the locking_thread's lock stack. - // * fast-locked - Coerce it to inflated from fast-locked. - // * unlocked - Aggressively inflate the object. - - // CASE: inflated - if (mark.has_monitor()) { - ObjectMonitor* inf = mark.monitor(); - markWord dmw = inf->header(); - assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value()); - if (inf->has_anonymous_owner() && - locking_thread != nullptr && locking_thread->lock_stack().contains(object)) { - inf->set_owner_from_anonymous(locking_thread); - size_t removed = locking_thread->lock_stack().remove(object); - inf->set_recursions(removed - 1); - } - return inf; - } - - // CASE: fast-locked - // Could be fast-locked either by the locking_thread or by some other thread. - // - // Note that we allocate the ObjectMonitor speculatively, _before_ - // attempting to set the object's mark to the new ObjectMonitor. If - // the locking_thread owns the monitor, then we set the ObjectMonitor's - // owner to the locking_thread. Otherwise, we set the ObjectMonitor's owner - // to anonymous. If we lose the race to set the object's mark to the - // new ObjectMonitor, then we just delete it and loop around again. - // - if (mark.is_fast_locked()) { - ObjectMonitor* monitor = new ObjectMonitor(object); - monitor->set_header(mark.set_unlocked()); - bool own = locking_thread != nullptr && locking_thread->lock_stack().contains(object); - if (own) { - // Owned by locking_thread. - monitor->set_owner(locking_thread); - } else { - // Owned by somebody else. - monitor->set_anonymous_owner(); - } - markWord monitor_mark = markWord::encode(monitor); - markWord old_mark = object->cas_set_mark(monitor_mark, mark); - if (old_mark == mark) { - // Success! Return inflated monitor. - if (own) { - size_t removed = locking_thread->lock_stack().remove(object); - monitor->set_recursions(removed - 1); - } - // Once the ObjectMonitor is configured and object is associated - // with the ObjectMonitor, it is safe to allow async deflation: - ObjectSynchronizer::_in_use_list.add(monitor); - - log_inflate(current, object, cause); - if (event.should_commit()) { - post_monitor_inflate_event(&event, object, cause); - } - return monitor; - } else { - delete monitor; - continue; // Interference -- just retry - } - } - - // CASE: unlocked - // TODO-FIXME: for entry we currently inflate and then try to CAS _owner. - // If we know we're inflating for entry it's better to inflate by swinging a - // pre-locked ObjectMonitor pointer into the object header. A successful - // CAS inflates the object *and* confers ownership to the inflating thread. - // In the current implementation we use a 2-step mechanism where we CAS() - // to inflate and then CAS() again to try to swing _owner from null to current. - // An inflateTry() method that we could call from enter() would be useful. - - assert(mark.is_unlocked(), "invariant: header=" INTPTR_FORMAT, mark.value()); - ObjectMonitor* m = new ObjectMonitor(object); - // prepare m for installation - set monitor to initial state - m->set_header(mark); - - if (object->cas_set_mark(markWord::encode(m), mark) != mark) { - delete m; - m = nullptr; - continue; - // interference - the markword changed - just retry. - // The state-transitions are one-way, so there's no chance of - // live-lock -- "Inflated" is an absorbing state. - } - - // Once the ObjectMonitor is configured and object is associated - // with the ObjectMonitor, it is safe to allow async deflation: - ObjectSynchronizer::_in_use_list.add(m); - - log_inflate(current, object, cause); - if (event.should_commit()) { - post_monitor_inflate_event(&event, object, cause); - } - return m; - } -} - ObjectMonitor* ObjectSynchronizer::inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current) { VerifyThreadState vts(locking_thread, current); assert(locking_thread->lock_stack().contains(object), "locking_thread must have object on its lock stack"); ObjectMonitor* monitor; - if (!UseObjectMonitorTable) { - return inflate_into_object_header(object, cause, locking_thread, current); - } - // Inflating requires a hash code ObjectSynchronizer::FastHashCode(current, object); @@ -2169,21 +1940,6 @@ ObjectMonitor* ObjectSynchronizer::inflate_and_enter(oop object, BasicLock* lock ObjectMonitor* monitor = nullptr; - if (!UseObjectMonitorTable) { - // Do the old inflate and enter. - monitor = inflate_into_object_header(object, cause, locking_thread, current); - - bool entered; - if (locking_thread == current) { - entered = monitor->enter(locking_thread); - } else { - entered = monitor->enter_for(locking_thread); - } - - // enter returns false for deflation found. - return entered ? monitor : nullptr; - } - NoSafepointVerifier nsv; // Try to get the monitor from the thread-local cache. @@ -2330,24 +2086,15 @@ void ObjectSynchronizer::deflate_monitor(oop obj, ObjectMonitor* monitor) { } ObjectMonitor* ObjectSynchronizer::get_monitor_from_table(oop obj) { - assert(UseObjectMonitorTable, "must be"); return ObjectMonitorTable::monitor_get(obj); } -ObjectMonitor* ObjectSynchronizer::read_monitor(markWord mark) { - return mark.monitor(); -} - ObjectMonitor* ObjectSynchronizer::read_monitor(oop obj) { return ObjectSynchronizer::read_monitor(obj, obj->mark()); } ObjectMonitor* ObjectSynchronizer::read_monitor(oop obj, markWord mark) { - if (!UseObjectMonitorTable) { - return read_monitor(mark); - } else { - return ObjectSynchronizer::get_monitor_from_table(obj); - } + return ObjectSynchronizer::get_monitor_from_table(obj); } bool ObjectSynchronizer::quick_enter_internal(oop obj, BasicLock* lock, JavaThread* current) { @@ -2381,26 +2128,19 @@ bool ObjectSynchronizer::quick_enter_internal(oop obj, BasicLock* lock, JavaThre #endif if (mark.has_monitor()) { - ObjectMonitor* monitor; - if (UseObjectMonitorTable) { - monitor = read_caches(current, lock, obj); - } else { - monitor = ObjectSynchronizer::read_monitor(mark); - } + ObjectMonitor* monitor = read_caches(current, lock, obj); if (monitor == nullptr) { // Take the slow-path on a cache miss. return false; } - if (UseObjectMonitorTable) { - // Set the monitor regardless of success. - // Either we successfully lock on the monitor, or we retry with the - // monitor in the slow path. If the monitor gets deflated, it will be - // cleared, either by the CacheSetter if we fast lock in enter or in - // inflate_and_enter when we see that the monitor is deflated. - lock->set_object_monitor_cache(monitor); - } + // Set the monitor regardless of success. + // Either we successfully lock on the monitor, or we retry with the + // monitor in the slow path. If the monitor gets deflated, it will be + // cleared, either by the CacheSetter if we fast lock in enter or in + // inflate_and_enter when we see that the monitor is deflated. + lock->set_object_monitor_cache(monitor); if (monitor->spin_enter(current)) { return true; diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 58d0e88a026e..922d988e290a 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -125,7 +125,6 @@ class ObjectSynchronizer : AllStatic { public: static const char* inflate_cause_name(const InflateCause cause); - static ObjectMonitor* read_monitor(markWord mark); static ObjectMonitor* read_monitor(oop obj); static ObjectMonitor* read_monitor(oop obj, markWord mark); @@ -234,7 +233,6 @@ class ObjectSynchronizer : AllStatic { static bool fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation); public: - static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current); static ObjectMonitor* inflate_locked_or_imse(oop object, ObjectSynchronizer::InflateCause cause, TRAPS); static ObjectMonitor* inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current); static ObjectMonitor* inflate_and_enter(oop object, BasicLock* lock, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 8c414e10ba9e..6663966f36e0 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -784,9 +784,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { if (CDSConfig::is_using_aot_linked_classes()) { AOTLinkedClassBulkLoader::init_non_javabase_classes(THREAD); } -#ifndef PRODUCT - HeapShared::initialize_test_class_from_archive(THREAD); -#endif JFR_ONLY(Jfr::on_create_vm_2();) diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 6ee592bd7740..5c9e6ad166a8 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1499,12 +1499,15 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } // Locks - Symbol* lock_sym = vmSymbols::jdk_internal_vm_ThreadLock(); - Klass* lock_k = SystemDictionary::resolve_or_fail(lock_sym, true, CHECK_NULL); - InstanceKlass* lock_klass = InstanceKlass::cast(lock_k); - refArrayHandle locks; if (cl._locks != nullptr && cl._locks->length() > 0) { + Symbol* lock_sym = vmSymbols::jdk_internal_vm_ThreadLock(); + Klass* lock_k = SystemDictionary::resolve_or_fail(lock_sym, true, CHECK_NULL); + if (lock_k->should_be_initialized()) { + lock_k->initialize(CHECK_NULL); + } + + InstanceKlass* lock_klass = InstanceKlass::cast(lock_k); locks = oopFactory::new_refArray_handle(lock_klass, cl._locks->length(), CHECK_NULL); for (int n = 0; n < cl._locks->length(); n++) { GetThreadSnapshotHandshakeClosure::OwnedLock* lock_info = cl._locks->adr_at(n); diff --git a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java index 7c833ad633ba..80ce42a8814c 100644 --- a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java @@ -127,7 +127,7 @@ private static int valueObjectHashCode(Object obj) { if (VERBOSE) { System.out.println("valueObjectHashCode: obj.getClass:" + obj.getClass().getName()); } - // This method assumes a is not null and is an instance of a value class + // This method assumes obj is not null and is an instance of a value class Class type = obj.getClass(); final Unsafe U = UNSAFE; int[] map = U.getFieldMap(type); diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index ffc5946b0b33..4db6c24ee1ef 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -27,6 +27,7 @@ import java.io.*; import java.net.*; +import java.net.Proxy.Type; import java.util.Locale; import java.util.Objects; import java.util.OptionalInt; @@ -174,6 +175,13 @@ int getKeepAliveTimeout() { return keepAliveTimeout; } + public Proxy getHttpProxy() { + if (proxy != null && proxy.type() == Type.HTTP) { + return proxy; + } + return null; + } + static String normalizeCBT(String s) { if (s == null || s.equals("never")) { return "never"; diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 45e641f11eee..7b7506420f72 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -366,6 +366,9 @@ private static Set schemesListToSet(String list) { private boolean tryTransparentNTLMProxy = true; private boolean useProxyResponseCode = false; + // used when redirecting to compare current and previous proxies + private Proxy lastProxy; + /* Used by Windows specific code */ private Object authObj; @@ -1376,7 +1379,6 @@ private InputStream getInputStream0() throws IOException { // If the user has set either of these headers then do not remove them isUserServerAuth = requests.getKey("Authorization") != -1; isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1; - try { do { if (!checkReuseConnection()) @@ -1386,6 +1388,14 @@ private InputStream getInputStream0() throws IOException { return cachedInputStream; } + // we may need to remove proxy-authorization + Proxy p = http.getHttpProxy(); + // if we're not using a proxy or if the proxy to be used is not + // the same as the originally set one, then remove it + if (p == null || (lastProxy != null && !lastProxy.equals(p))) { + requests.remove("Proxy-Authorization"); + lastProxy = null; + } /* REMIND: This exists to fix the HttpsURLConnection subclass. * Hotjava needs to run on JDK1.1FCS. Do proper fix once a * proper solution for SSL can be found. @@ -1416,7 +1426,7 @@ private InputStream getInputStream0() throws IOException { disconnectInternal(); throw new IOException ("Invalid Http response"); } - if (respCode == HTTP_PROXY_AUTH) { + if (respCode == HTTP_PROXY_AUTH && tunnelState() != TunnelState.TUNNELING) { if (streaming()) { disconnectInternal(); throw new HttpRetryException ( @@ -1999,6 +2009,7 @@ private void doTunneling0() throws IOException { if (respCode == HTTP_OK) { setTunnelState(TunnelState.TUNNELING); + savedRequests.remove("Proxy-Authorization"); break; } // we don't know how to deal with other response code @@ -2552,6 +2563,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl) { assert isLockHeldByCurrentThread(); + lastProxy = http.getHttpProxy(); disconnectInternal(); if (streaming()) { throw new HttpRetryException (RETRY_MSG3, stat, loc); diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java index 8bb7def0f575..5d203b5c6ccd 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ class ServerHandshakeContext extends HandshakeContext { private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; final long statusRespTimeout; boolean acceptCliHelloFragments = false; - + boolean sentHRR = false; ServerHandshakeContext(SSLContextImpl sslContext, TransportContext conContext) throws IOException { diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 4bd2b0a059f5..360699280512 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -805,6 +805,15 @@ private T13HelloRetryRequestProducer() { public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { ServerHandshakeContext shc = (ServerHandshakeContext) context; + + + if (shc.sentHRR) { + throw shc.conContext.fatal( + Alert.HANDSHAKE_FAILURE, + "TLS 1.3 server MUST NOT send a second HelloRetryRequest " + + "in the same connection"); + } + ClientHelloMessage clientHello = (ClientHelloMessage) message; // negotiate the cipher suite. @@ -840,6 +849,7 @@ public byte[] produce(ConnectionContext context, // Output the handshake message. hhrm.write(shc.handshakeOutput); shc.handshakeOutput.flush(); + shc.sentHRR = true; // In TLS1.3 middlebox compatibility mode the server sends a // dummy change_cipher_spec record immediately after its diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java index 9a6c0cf37649..cd84103743f7 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, 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 @@ -272,8 +272,8 @@ void setAttributes(WindowsFileAttributes attrs) // as otherwise the last modified time may be wrong. setFileTimes( WindowsFileAttributes.toWindowsTime(attrs.creationTime()), - WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()), - WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime())); + WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()), + WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime())); } } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java index a561e81536ab..41557733c171 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,11 +170,15 @@ static void copy(final WindowsPath source, } } + if (!followLinks && sourceAttrs.isSymbolicLink()) { + copySymbolicLink(source, sourceAttrs, target, copyAttributes); + return; + } + // Use CopyFileEx if the file is not a directory or junction if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) { boolean isBuffering = sourceAttrs.size() <= UNBUFFERED_IO_THRESHOLD; - final int flags = (followLinks ? 0 : COPY_FILE_COPY_SYMLINK) | - (isBuffering ? 0 : COPY_FILE_NO_BUFFERING); + final int flags = isBuffering ? 0 : COPY_FILE_NO_BUFFERING; if (interruptible) { // interruptible copy @@ -246,6 +250,7 @@ public void implRun() throws IOException { RemoveDirectory(targetPath); } catch (WindowsException ignore) { } } + throw x; } // copy security attributes. If this fail it doesn't cause the move @@ -256,6 +261,45 @@ public void implRun() throws IOException { } } + private static void copySymbolicLink(WindowsPath source, + WindowsFileAttributes sourceAttrs, + WindowsPath target, + boolean copyAttributes) + throws IOException + { + String targetPath = asWin32Path(target); + try { + String linkTarget = WindowsLinkSupport.readLink(source); + int flags = sourceAttrs.isDirectoryLink() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; + WindowsLinkSupport.createSymbolicLink(targetPath, + WindowsPath.addPrefixIfNeeded(linkTarget), + flags); + } catch (WindowsException x) { + x.rethrowAsIOException(target); + } + + if (copyAttributes) { + WindowsFileAttributeViews.Dos view = + WindowsFileAttributeViews.createDosView(target, false); + try { + view.setAttributes(sourceAttrs); + } catch (IOException x) { + try { + if (sourceAttrs.isDirectoryLink()) { + RemoveDirectory(targetPath); + } else { + DeleteFile(targetPath); + } + } catch (WindowsException ignore) { } + throw x; + } + + try { + copySecurityAttributes(source, target, false); + } catch (IOException ignore) { } + } + } + // throw a DirectoryNotEmpty exception if not empty static void ensureEmptyDir(WindowsPath dir) throws IOException { try (WindowsDirectoryStream dirStream = diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java index 634f578df025..61f9d543394e 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -1359,7 +1359,7 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) { if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { focusLog.fine("ungrabbing on " + grabbingWindow); } - // ungrab a simple window if its owner looses activation. + // ungrab a simple window if its owner loses activation. grabbingWindow.ungrab(); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h index e71224a6ff3f..ecd716ca06c5 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 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 @@ -101,8 +101,8 @@ struct _ImageSDOps CGDataProviderRef dataProvider; // Pointer in memory that is used for create the CGBitmapContext and the CGDataProvider (used for imgRef). This is a native - // copy of the pixels for the Image. There is a spearate copy of the pixels that lives in Java heap. There are two main - // reasons why we keep those pixels spearate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can + // copy of the pixels for the Image. There is a separate copy of the pixels that lives in Java heap. There are two main + // reasons why we keep those pixels separate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can // move the java pixels at any time. There are possible workarounds for both problems. Number 2) seems to be a more serious issue, since // we can solve 1) by only supporting certain image types. void * nativePixels; diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java index 730294c9f017..7424e74f1d8b 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1810,7 +1810,7 @@ private boolean isValidKeyword(String s) { * Latin-1 [ISO-8859-1] characters and spaces; that is, only * character codes 32-126 and 161-255 decimal are allowed. * For Latin-1 value fields the 0x10 (linefeed) control - * character is aloowed too. + * character is allowed too. * * See: http://www.w3.org/TR/PNG/#11keywords */ diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java index c5f8a0991745..6e21ce3ed854 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.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 @@ -377,7 +377,7 @@ public abstract class TIFFDecompressor { /** * The width of the source region that will actually be copied * into the destination image, taking into account all - * susbampling, offsetting, and clipping. + * subsampling, offsetting, and clipping. * *

    The active source width will always be equal to * {@code (dstWidth - 1)*subsampleX + 1}. diff --git a/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java b/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java index 257777628b29..f22a48d3fdaa 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, 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 @@ -26,7 +26,7 @@ package com.sun.media.sound; /** - * This class is used to store modulator/artiuclation data. + * This class is used to store modulator/articulation data. * A modulator connects one synthesizer source to * a destination. For example a note on velocity * can be mapped to the gain of the synthesized voice. diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 4f4b82164868..b52251ad7e1e 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, 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 @@ -1221,7 +1221,7 @@ public AudioInputStream openStream(AudioFormat targetFormat, // Always create external_channels array // with 16 or more channels // so getChannels works correctly - // when the synhtesizer is closed. + // when the synthesizer is closed. if (channels.length < 16) external_channels = new SoftChannelProxy[16]; else diff --git a/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java b/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java index 7295b180ed13..c4bdebbc8f82 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, 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 @@ -48,7 +48,7 @@ public final class WaveFileReader extends SunFileReader { StandardFileFormat getAudioFileFormatImpl(final InputStream stream) throws UnsupportedAudioFileException, IOException { - // assumes sream is rewound + // assumes stream is rewound int nread = 0; int fmt; diff --git a/src/java.desktop/share/classes/java/awt/Dialog.java b/src/java.desktop/share/classes/java/awt/Dialog.java index 038aa5b65e32..3230d94228c8 100644 --- a/src/java.desktop/share/classes/java/awt/Dialog.java +++ b/src/java.desktop/share/classes/java/awt/Dialog.java @@ -908,7 +908,7 @@ private boolean conditionalShow(Component toFocus, AtomicLong time) { } // This call is required as the show() method of the Dialog class - // does not invoke the super.show(). So wried... :( + // does not invoke the super.show(). So weird... :( mixOnShowing(); peer.setVisible(true); // now guaranteed never to block diff --git a/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java b/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java index d7dc4a4fba7c..92af1b327d87 100644 --- a/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java +++ b/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2023, 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 @@ -648,7 +648,7 @@ public int getBlue(Object inData) { * {@code inData} is not large enough to hold a pixel value * for this {@code ColorModel} * @throws UnsupportedOperationException if this - * {@code tranferType} is not supported by this + * {@code transferType} is not supported by this * {@code ColorModel} */ public int getAlpha(Object inData) { diff --git a/src/java.desktop/share/classes/java/beans/EventHandler.java b/src/java.desktop/share/classes/java/beans/EventHandler.java index 60f4e7bafdae..8581fd250a8a 100644 --- a/src/java.desktop/share/classes/java/beans/EventHandler.java +++ b/src/java.desktop/share/classes/java/beans/EventHandler.java @@ -229,7 +229,7 @@ * * * The target property may also be "qualified" with an arbitrary number - * of property prefixs delimited with the "." character. For example, the + * of property prefixes delimited with the "." character. For example, the * following action listener: *

      *   EventHandler.create(ActionListener.class, target, "a.b", "c.d")
    diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
    index 17897f6865e8..1b2ab2885e60 100644
    --- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
    +++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
    @@ -1285,7 +1285,7 @@ private synchronized void readObject(ObjectInputStream ois) throws IOException,
         protected transient HashMap  services;
     
         /**
    -     * The number of instances of a serializable {@code BeanContextServceProvider}.
    +     * The number of instances of a serializable {@code BeanContextServiceProvider}.
          */
         protected transient int                      serializable = 0;
     
    diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
    index 884b36d4b12b..d289bc8e116b 100644
    --- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
    +++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
    @@ -374,7 +374,7 @@ protected class BCSChild implements Serializable {
          * methods that add children to the set.
          * 

    * @param targetChild the child to create the Child on behalf of - * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy + * @param peer the peer if the targetChild and the peer are related by an implementation of BeanContextProxy * @return Subtype-specific subclass of Child without overriding collection methods */ diff --git a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java index f378e969a5a0..84c66721efe1 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1195,7 +1195,7 @@ public void prepareWriteEmpty(IIOMetadata streamMetadata, * {@code prepareInsertEmpty} without a corresponding call to * {@code endInsertEmpty} has been made. * @throws IllegalStateException if a call to - * {@code prepareReiplacePixels} has been made without a + * {@code prepareReplacePixels} has been made without a * matching call to {@code endReplacePixels}. * @throws IOException if an I/O error occurs during writing. */ diff --git a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java index b0cd40b6f6eb..06010a5da67c 100644 --- a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java +++ b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, 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 @@ -443,7 +443,7 @@ public interface IIOMetadataFormat { * interpretation of the value of the given attribute within the * named element. If {@code getAttributeValueType} returns * {@code VALUE_LIST}, then the legal value is a - * whitespace-spearated list of values of the returned datatype. + * whitespace-separated list of values of the returned datatype. * * @param elementName the name of the element being queried. * @param attrName the name of the attribute being queried. diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java index ebeb456b164b..854e1816b99e 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.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 @@ -314,21 +314,21 @@ public final class BaselineTIFFTagSet extends TIFFTagSet { public static final int TAG_THRESHHOLDING = 263; /** - * A value to be used with the "Thresholding" tag. + * A value to be used with the "Threshholding" tag. * * @see #TAG_THRESHHOLDING */ public static final int THRESHHOLDING_NONE = 1; /** - * A value to be used with the "Thresholding" tag. + * A value to be used with the "Threshholding" tag. * * @see #TAG_THRESHHOLDING */ public static final int THRESHHOLDING_ORDERED_DITHER = 2; /** - * A value to be used with the "Thresholding" tag. + * A value to be used with the "Threshholding" tag. * * @see #TAG_THRESHHOLDING */ diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java index 197b351feb72..4d4765b7ef90 100644 --- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java +++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.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 @@ -275,7 +275,7 @@ public void removeTagSet(TIFFTagSet tagSet) { * has been defined or {@code null} otherwise. * * @return The parent {@code TIFFTag} of this - * {@code TIFFDiectory} or {@code null}. + * {@code TIFFDirectory} or {@code null}. */ public TIFFTag getParentTag() { return parentTag; diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java b/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java index fb3b8d58e311..de82ed71f2c1 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, 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 @@ -113,7 +113,7 @@ public Class getInputClass() { * Returns {@code true} if the {@code ImageInputStream} * implementation associated with this service provider can * optionally make use of a cache file for improved performance - * and/or memory footrprint. If {@code false}, the value of + * and/or memory footprint. If {@code false}, the value of * the {@code useCache} argument to * {@code createInputStreamInstance} will be ignored. * diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java b/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java index a8fcf7ef19f7..7b795d889943 100644 --- a/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java +++ b/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, 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 @@ -115,7 +115,7 @@ public Class getOutputClass() { * Returns {@code true} if the {@code ImageOutputStream} * implementation associated with this service provider can * optionally make use of a cache {@code File} for improved - * performance and/or memory footrprint. If {@code false}, + * performance and/or memory footprint. If {@code false}, * the value of the {@code cacheFile} argument to * {@code createOutputStreamInstance} will be ignored. * diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java index de5f705faccf..adbbb2c0a77b 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,6 +150,7 @@ private long readUntil(long pos) throws IOException { return pos; } + @Override public int read() throws IOException { checkClosed(); bitOffset = 0; @@ -163,6 +164,7 @@ public int read() throws IOException { } } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); @@ -205,6 +207,7 @@ public int read(byte[] b, int off, int len) throws IOException { * @see #isCachedMemory * @see #isCachedFile */ + @Override public boolean isCached() { return true; } @@ -218,6 +221,7 @@ public boolean isCached() { * @see #isCached * @see #isCachedMemory */ + @Override public boolean isCachedFile() { return true; } @@ -232,6 +236,7 @@ public boolean isCachedFile() { * @see #isCached * @see #isCachedFile */ + @Override public boolean isCachedMemory() { return false; } @@ -243,6 +248,7 @@ public boolean isCachedMemory() { * * @throws IOException if an error occurs. */ + @Override public void close() throws IOException { super.close(); disposerRecord.dispose(); // this will close/delete the cache file @@ -261,6 +267,7 @@ public StreamDisposerRecord(File cacheFile, RandomAccessFile cache) { this.cache = cache; } + @Override public synchronized void dispose() { if (cache != null) { try { diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java index 8c80d3488e1f..a0d330c85499 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,6 +108,7 @@ public FileCacheImageOutputStream(OutputStream stream, File cacheDir) StreamCloser.addToQueue(closeAction); } + @Override public int read() throws IOException { checkClosed(); bitOffset = 0; @@ -118,6 +119,7 @@ public int read() throws IOException { return val; } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); @@ -142,6 +144,7 @@ public int read(byte[] b, int off, int len) throws IOException { return nbytes; } + @Override public void write(int b) throws IOException { flushBits(); // this will call checkClosed() for us cache.write(b); @@ -149,6 +152,7 @@ public void write(int b) throws IOException { maxStreamPos = Math.max(maxStreamPos, streamPos); } + @Override public void write(byte[] b, int off, int len) throws IOException { flushBits(); // this will call checkClosed() for us cache.write(b, off, len); @@ -156,6 +160,7 @@ public void write(byte[] b, int off, int len) throws IOException { maxStreamPos = Math.max(maxStreamPos, streamPos); } + @Override public long length() { try { checkClosed(); @@ -176,6 +181,7 @@ public long length() { * than the flushed position. * @throws IOException if any other I/O error occurs. */ + @Override public void seek(long pos) throws IOException { checkClosed(); @@ -199,6 +205,7 @@ public void seek(long pos) throws IOException { * @see #isCachedMemory * @see #isCachedFile */ + @Override public boolean isCached() { return true; } @@ -212,6 +219,7 @@ public boolean isCached() { * @see #isCached * @see #isCachedMemory */ + @Override public boolean isCachedFile() { return true; } @@ -226,6 +234,7 @@ public boolean isCachedFile() { * @see #isCached * @see #isCachedFile */ + @Override public boolean isCachedMemory() { return false; } @@ -238,6 +247,7 @@ public boolean isCachedMemory() { * * @throws IOException if an error occurs. */ + @Override public void close() throws IOException { maxStreamPos = cache.length(); @@ -257,6 +267,7 @@ public void close() throws IOException { * @param pos {@inheritDoc ImageOutputStream} * @throws IOException {@inheritDoc ImageOutputStream} */ + @Override public void flushBefore(long pos) throws IOException { long oFlushedPos = flushedPos; super.flushBefore(pos); // this will call checkClosed() for us diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java index e5baee6e7c09..346ad7d09b78 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,6 +97,7 @@ public FileImageInputStream(RandomAccessFile raf) { Disposer.addRecord(disposerReferent, disposerRecord); } + @Override public int read() throws IOException { checkClosed(); bitOffset = 0; @@ -107,6 +108,7 @@ public int read() throws IOException { return val; } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); bitOffset = 0; @@ -124,6 +126,7 @@ public int read(byte[] b, int off, int len) throws IOException { * @return the file length as a {@code long}, or * {@code -1}. */ + @Override public long length() { try { checkClosed(); @@ -133,6 +136,7 @@ public long length() { } } + @Override public void seek(long pos) throws IOException { checkClosed(); if (pos < flushedPos) { @@ -143,6 +147,7 @@ public void seek(long pos) throws IOException { streamPos = raf.getFilePointer(); } + @Override public void close() throws IOException { super.close(); disposerRecord.dispose(); // this closes the RandomAccessFile diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java index 8a22afd5b122..990f3e681448 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,6 +89,7 @@ public FileImageOutputStream(RandomAccessFile raf) { Disposer.addRecord(disposerReferent, disposerRecord); } + @Override public int read() throws IOException { checkClosed(); bitOffset = 0; @@ -99,6 +100,7 @@ public int read() throws IOException { return val; } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); bitOffset = 0; @@ -109,18 +111,21 @@ public int read(byte[] b, int off, int len) throws IOException { return nbytes; } + @Override public void write(int b) throws IOException { flushBits(); // this will call checkClosed() for us raf.write(b); ++streamPos; } + @Override public void write(byte[] b, int off, int len) throws IOException { flushBits(); // this will call checkClosed() for us raf.write(b, off, len); streamPos += len; } + @Override public long length() { try { checkClosed(); @@ -141,6 +146,7 @@ public long length() { * than the flushed position. * @throws IOException if any other I/O error occurs. */ + @Override public void seek(long pos) throws IOException { checkClosed(); if (pos < flushedPos) { @@ -151,6 +157,7 @@ public void seek(long pos) throws IOException { streamPos = raf.getFilePointer(); } + @Override public void close() throws IOException { super.close(); disposerRecord.dispose(); // this closes the RandomAccessFile diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java index 11251ac5938a..e648c5c00b49 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -186,6 +186,7 @@ public interface ImageInputStream extends DataInput, Closeable { * @throws java.io.EOFException if the end of the stream is reached. * @throws IOException if an I/O error occurs. */ + @Override boolean readBoolean() throws IOException; /** @@ -204,6 +205,7 @@ public interface ImageInputStream extends DataInput, Closeable { * @throws java.io.EOFException if the end of the stream is reached. * @throws IOException if an I/O error occurs. */ + @Override byte readByte() throws IOException; /** @@ -228,6 +230,7 @@ public interface ImageInputStream extends DataInput, Closeable { * @throws java.io.EOFException if the end of the stream is reached. * @throws IOException if an I/O error occurs. */ + @Override int readUnsignedByte() throws IOException; /** @@ -246,6 +249,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override short readShort() throws IOException; /** @@ -267,6 +271,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override int readUnsignedShort() throws IOException; /** @@ -284,6 +289,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #readUnsignedShort */ + @Override char readChar() throws IOException; /** @@ -302,6 +308,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override int readInt() throws IOException; /** @@ -340,6 +347,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override long readLong() throws IOException; /** @@ -358,6 +366,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override float readFloat() throws IOException; /** @@ -376,6 +385,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @see #getByteOrder */ + @Override double readDouble() throws IOException; /** @@ -410,6 +420,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @throws IOException if an I/O error occurs. */ + @Override String readLine() throws IOException; /** @@ -494,6 +505,7 @@ public interface ImageInputStream extends DataInput, Closeable { * a valid modified UTF-8 encoding of a string. * @throws IOException if an I/O error occurs. */ + @Override String readUTF() throws IOException; /** @@ -518,6 +530,7 @@ public interface ImageInputStream extends DataInput, Closeable { * reading all the bytes. * @throws IOException if an I/O error occurs. */ + @Override void readFully(byte[] b, int off, int len) throws IOException; /** @@ -537,6 +550,7 @@ public interface ImageInputStream extends DataInput, Closeable { * reading all the bytes. * @throws IOException if an I/O error occurs. */ + @Override void readFully(byte[] b) throws IOException; /** @@ -822,6 +836,7 @@ public interface ImageInputStream extends DataInput, Closeable { * * @throws IOException if an I/O error occurs. */ + @Override int skipBytes(int n) throws IOException; /** @@ -995,5 +1010,6 @@ public interface ImageInputStream extends DataInput, Closeable { * * @throws IOException if an I/O error occurs. */ + @Override void close() throws IOException; } diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java index 103fe4019e40..c672bcac4a57 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,10 +114,12 @@ protected final void checkClosed() throws IOException { } } + @Override public void setByteOrder(ByteOrder byteOrder) { this.byteOrder = byteOrder; } + @Override public ByteOrder getByteOrder() { return byteOrder; } @@ -139,6 +141,7 @@ public ByteOrder getByteOrder() { * * @throws IOException if the stream has been closed. */ + @Override public abstract int read() throws IOException; /** @@ -154,6 +157,7 @@ public ByteOrder getByteOrder() { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @@ -185,8 +189,10 @@ public int read(byte[] b) throws IOException { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override public abstract int read(byte[] b, int off, int len) throws IOException; + @Override public void readBytes(IIOByteBuffer buf, int len) throws IOException { if (len < 0) { throw new IndexOutOfBoundsException("len < 0!"); @@ -206,6 +212,7 @@ public void readBytes(IIOByteBuffer buf, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public boolean readBoolean() throws IOException { int ch = this.read(); if (ch < 0) { @@ -217,6 +224,7 @@ public boolean readBoolean() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public byte readByte() throws IOException { int ch = this.read(); if (ch < 0) { @@ -228,6 +236,7 @@ public byte readByte() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public int readUnsignedByte() throws IOException { int ch = this.read(); if (ch < 0) { @@ -239,6 +248,7 @@ public int readUnsignedByte() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public short readShort() throws IOException { if (read(byteBuf, 0, 2) != 2) { throw new EOFException(); @@ -251,6 +261,7 @@ public short readShort() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public int readUnsignedShort() throws IOException { return ((int)readShort()) & 0xffff; } @@ -258,6 +269,7 @@ public int readUnsignedShort() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public char readChar() throws IOException { return (char)readShort(); } @@ -265,6 +277,7 @@ public char readChar() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public int readInt() throws IOException { if (read(byteBuf, 0, 4) != 4) { throw new EOFException(); @@ -278,6 +291,7 @@ public int readInt() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public long readUnsignedInt() throws IOException { return ((long)readInt()) & 0xffffffffL; } @@ -285,6 +299,7 @@ public long readUnsignedInt() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public long readLong() throws IOException { // REMIND: Once 6277756 is fixed, we should do a bulk read of all 8 // bytes here as we do in readShort() and readInt() for even better @@ -302,6 +317,7 @@ public long readLong() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } @@ -309,10 +325,12 @@ public float readFloat() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } + @Override public String readLine() throws IOException { StringBuilder input = new StringBuilder(); int c = -1; @@ -347,6 +365,7 @@ public String readLine() throws IOException { * @throws EOFException {@inheritDoc} * @throws java.io.UTFDataFormatException {@inheritDoc} */ + @Override public String readUTF() throws IOException { this.bitOffset = 0; @@ -372,6 +391,7 @@ public String readUTF() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(byte[] b, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > b.length || off + len < 0) { @@ -392,6 +412,7 @@ public void readFully(byte[] b, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(byte[] b) throws IOException { readFully(b, 0, b.length); } @@ -399,6 +420,7 @@ public void readFully(byte[] b) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(short[] s, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > s.length || off + len < 0) { @@ -418,6 +440,7 @@ public void readFully(short[] s, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(char[] c, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > c.length || off + len < 0) { @@ -437,6 +460,7 @@ public void readFully(char[] c, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(int[] i, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > i.length || off + len < 0) { @@ -456,6 +480,7 @@ public void readFully(int[] i, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(long[] l, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > l.length || off + len < 0) { @@ -475,6 +500,7 @@ public void readFully(long[] l, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(float[] f, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > f.length || off + len < 0) { @@ -494,6 +520,7 @@ public void readFully(float[] f, int off, int len) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public void readFully(double[] d, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > d.length || off + len < 0) { @@ -600,16 +627,19 @@ private void toDoubles(byte[] b, double[] d, int off, int len) { } } + @Override public long getStreamPosition() throws IOException { checkClosed(); return streamPos; } + @Override public int getBitOffset() throws IOException { checkClosed(); return bitOffset; } + @Override public void setBitOffset(int bitOffset) throws IOException { checkClosed(); if (bitOffset < 0 || bitOffset > 7) { @@ -621,6 +651,7 @@ public void setBitOffset(int bitOffset) throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public int readBit() throws IOException { checkClosed(); @@ -646,6 +677,7 @@ public int readBit() throws IOException { /** * @throws EOFException {@inheritDoc} */ + @Override public long readBits(int numBits) throws IOException { checkClosed(); @@ -697,6 +729,7 @@ public long readBits(int numBits) throws IOException { * * @return -1L to indicate unknown length. */ + @Override public long length() { return -1L; } @@ -716,6 +749,7 @@ public long length() { * throws an {@code IOException} when computing either * the starting or ending position. */ + @Override public int skipBytes(int n) throws IOException { long pos = getStreamPosition(); seek(pos + n); @@ -737,12 +771,14 @@ public int skipBytes(int n) throws IOException { * throws an {@code IOException} when computing either * the starting or ending position. */ + @Override public long skipBytes(long n) throws IOException { long pos = getStreamPosition(); seek(pos + n); return getStreamPosition() - pos; } + @Override public void seek(long pos) throws IOException { checkClosed(); @@ -759,6 +795,7 @@ public void seek(long pos) throws IOException { * Pushes the current stream position onto a stack of marked * positions. */ + @Override public void mark() { try { markByteStack.push(Long.valueOf(getStreamPosition())); @@ -776,6 +813,7 @@ public void mark() { * * @throws IOException if an I/O error occurs. */ + @Override public void reset() throws IOException { if (markByteStack.empty()) { return; @@ -792,6 +830,7 @@ public void reset() throws IOException { setBitOffset(offset); } + @Override public void flushBefore(long pos) throws IOException { checkClosed(); if (pos < flushedPos) { @@ -804,10 +843,12 @@ public void flushBefore(long pos) throws IOException { flushedPos = pos; } + @Override public void flush() throws IOException { flushBefore(getStreamPosition()); } + @Override public long getFlushedPosition() { return flushedPos; } @@ -816,6 +857,7 @@ public long getFlushedPosition() { * Default implementation returns false. Subclasses should * override this if they cache data. */ + @Override public boolean isCached() { return false; } @@ -824,6 +866,7 @@ public boolean isCached() { * Default implementation returns false. Subclasses should * override this if they cache data in main memory. */ + @Override public boolean isCachedMemory() { return false; } @@ -832,10 +875,12 @@ public boolean isCachedMemory() { * Default implementation returns false. Subclasses should * override this if they cache data in a temporary file. */ + @Override public boolean isCachedFile() { return false; } + @Override public void close() throws IOException { checkClosed(); diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java index d6ccf5ee524a..39a1c0ff15ec 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2018, 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 @@ -68,6 +68,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void write(int b) throws IOException; /** @@ -87,6 +88,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override void write(byte[] b) throws IOException; /** @@ -114,6 +116,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override void write(byte[] b, int off, int len) throws IOException; /** @@ -131,6 +134,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeBoolean(boolean v) throws IOException; /** @@ -149,6 +153,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeByte(int v) throws IOException; /** @@ -179,6 +184,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeShort(int v) throws IOException; /** @@ -191,6 +197,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @see #writeShort(int) */ + @Override void writeChar(int v) throws IOException; /** @@ -224,6 +231,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeInt(int v) throws IOException; /** @@ -265,6 +273,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeLong(long v) throws IOException; /** @@ -285,6 +294,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeFloat(float v) throws IOException; /** @@ -306,6 +316,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * * @throws IOException if an I/O error occurs. */ + @Override void writeDouble(double v) throws IOException; /** @@ -334,6 +345,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override void writeBytes(String s) throws IOException; /** @@ -362,6 +374,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * {@code null}. * @throws IOException if an I/O error occurs. */ + @Override void writeChars(String s) throws IOException; /** @@ -433,6 +446,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * representation of {@code s} requires more than 65536 bytes. * @throws IOException if an I/O error occurs. */ + @Override void writeUTF(String s) throws IOException; /** @@ -658,5 +672,6 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput { * position. * @throws IOException if an I/O error occurs. */ + @Override void flushBefore(long pos) throws IOException; } diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java index 13231470a7aa..0295d77b1867 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,22 +48,28 @@ public abstract class ImageOutputStreamImpl public ImageOutputStreamImpl() { } + @Override public abstract void write(int b) throws IOException; + @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } + @Override public abstract void write(byte[] b, int off, int len) throws IOException; + @Override public void writeBoolean(boolean v) throws IOException { write(v ? 1 : 0); } + @Override public void writeByte(int v) throws IOException { write(v); } + @Override public void writeShort(int v) throws IOException { if (byteOrder == ByteOrder.BIG_ENDIAN) { ByteArray.setUnsignedShort(byteBuf, 0, v); @@ -73,10 +79,12 @@ public void writeShort(int v) throws IOException { write(byteBuf, 0, 2); } + @Override public void writeChar(int v) throws IOException { writeShort(v); } + @Override public void writeInt(int v) throws IOException { if (byteOrder == ByteOrder.BIG_ENDIAN) { ByteArray.setInt(byteBuf, 0, v); @@ -86,6 +94,7 @@ public void writeInt(int v) throws IOException { write(byteBuf, 0, 4); } + @Override public void writeLong(long v) throws IOException { if (byteOrder == ByteOrder.BIG_ENDIAN) { ByteArray.setLong(byteBuf, 0, v); @@ -100,14 +109,17 @@ public void writeLong(long v) throws IOException { write(byteBuf, 4, 4); } + @Override public void writeFloat(float v) throws IOException { writeInt(Float.floatToIntBits(v)); } + @Override public void writeDouble(double v) throws IOException { writeLong(Double.doubleToLongBits(v)); } + @Override public void writeBytes(String s) throws IOException { int len = s.length(); for (int i = 0 ; i < len ; i++) { @@ -115,6 +127,7 @@ public void writeBytes(String s) throws IOException { } } + @Override public void writeChars(String s) throws IOException { int len = s.length(); @@ -140,6 +153,7 @@ public void writeChars(String s) throws IOException { /** * @throws UTFDataFormatException {@inheritDoc} */ + @Override public void writeUTF(String s) throws IOException { int strlen = s.length(); int utflen = 0; @@ -182,6 +196,7 @@ public void writeUTF(String s) throws IOException { write(b, 0, utflen + 2); } + @Override public void writeShorts(short[] s, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > s.length || off + len < 0) { @@ -208,6 +223,7 @@ public void writeShorts(short[] s, int off, int len) throws IOException { write(b, 0, len*2); } + @Override public void writeChars(char[] c, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > c.length || off + len < 0) { @@ -234,6 +250,7 @@ public void writeChars(char[] c, int off, int len) throws IOException { write(b, 0, len*2); } + @Override public void writeInts(int[] i, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > i.length || off + len < 0) { @@ -260,6 +277,7 @@ public void writeInts(int[] i, int off, int len) throws IOException { write(b, 0, len*4); } + @Override public void writeLongs(long[] l, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > l.length || off + len < 0) { @@ -286,6 +304,7 @@ public void writeLongs(long[] l, int off, int len) throws IOException { write(b, 0, len*8); } + @Override public void writeFloats(float[] f, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > f.length || off + len < 0) { @@ -312,6 +331,7 @@ public void writeFloats(float[] f, int off, int len) throws IOException { write(b, 0, len*4); } + @Override public void writeDoubles(double[] d, int off, int len) throws IOException { // Fix 4430357 - if off + len < 0, overflow occurred if (off < 0 || len < 0 || off + len > d.length || off + len < 0) { @@ -338,10 +358,12 @@ public void writeDoubles(double[] d, int off, int len) throws IOException { write(b, 0, len*8); } + @Override public void writeBit(int bit) throws IOException { writeBits((1L & bit), 1); } + @Override public void writeBits(long bits, int numBits) throws IOException { checkClosed(); diff --git a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java index 0f259a9ce464..9e4343add7a4 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,6 +62,7 @@ public MemoryCacheImageInputStream(InputStream stream) { this.stream = stream; } + @Override public int read() throws IOException { checkClosed(); bitOffset = 0; @@ -73,6 +74,7 @@ public int read() throws IOException { } } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); @@ -103,6 +105,7 @@ public int read(byte[] b, int off, int len) throws IOException { } } + @Override public void flushBefore(long pos) throws IOException { super.flushBefore(pos); // this will call checkClosed() for us cache.disposeBefore(pos); @@ -118,6 +121,7 @@ public void flushBefore(long pos) throws IOException { * @see #isCachedMemory * @see #isCachedFile */ + @Override public boolean isCached() { return true; } @@ -131,6 +135,7 @@ public boolean isCached() { * @see #isCached * @see #isCachedMemory */ + @Override public boolean isCachedFile() { return false; } @@ -144,6 +149,7 @@ public boolean isCachedFile() { * @see #isCached * @see #isCachedFile */ + @Override public boolean isCachedMemory() { return true; } @@ -152,6 +158,7 @@ public boolean isCachedMemory() { * Closes this {@code MemoryCacheImageInputStream}, freeing * the cache. The source {@code InputStream} is not closed. */ + @Override public void close() throws IOException { super.close(); stream = null; diff --git a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java index dd7049689b4e..c61cc3110a6f 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, 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 @@ -61,6 +61,7 @@ public MemoryCacheImageOutputStream(OutputStream stream) { this.stream = stream; } + @Override public int read() throws IOException { checkClosed(); @@ -73,6 +74,7 @@ public int read() throws IOException { return val; } + @Override public int read(byte[] b, int off, int len) throws IOException { checkClosed(); @@ -107,18 +109,21 @@ public int read(byte[] b, int off, int len) throws IOException { return len; } + @Override public void write(int b) throws IOException { flushBits(); // this will call checkClosed() for us cache.write(b, streamPos); ++streamPos; } + @Override public void write(byte[] b, int off, int len) throws IOException { flushBits(); // this will call checkClosed() for us cache.write(b, off, len, streamPos); streamPos += len; } + @Override public long length() { try { checkClosed(); @@ -138,6 +143,7 @@ public long length() { * @see #isCachedMemory * @see #isCachedFile */ + @Override public boolean isCached() { return true; } @@ -151,6 +157,7 @@ public boolean isCached() { * @see #isCached * @see #isCachedMemory */ + @Override public boolean isCachedFile() { return false; } @@ -164,6 +171,7 @@ public boolean isCachedFile() { * @see #isCached * @see #isCachedFile */ + @Override public boolean isCachedMemory() { return true; } @@ -174,6 +182,7 @@ public boolean isCachedMemory() { * is released. The destination {@code OutputStream} * is not closed. */ + @Override public void close() throws IOException { long length = cache.getLength(); seek(length); @@ -189,6 +198,7 @@ public void close() throws IOException { * @param pos {@inheritDoc ImageOutputStream} * @throws IOException {@inheritDoc ImageOutputStream} */ + @Override public void flushBefore(long pos) throws IOException { long oFlushedPos = flushedPos; super.flushBefore(pos); // this will call checkClosed() for us diff --git a/src/java.desktop/share/classes/javax/sound/SoundClip.java b/src/java.desktop/share/classes/javax/sound/SoundClip.java index 41ad11e5cacf..597de69d244a 100644 --- a/src/java.desktop/share/classes/javax/sound/SoundClip.java +++ b/src/java.desktop/share/classes/javax/sound/SoundClip.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 @@ -123,7 +123,7 @@ public void play() { *

    * Threading notes : Most applications will not need to do anything except call {@code loop()}. * The following is therefore something most applications need not be concerned about. - * Play back is managed in a background thread, which is ususally a daemon thread. + * Play back is managed in a background thread, which is usually a daemon thread. * Running daemon threads do not prevent the VM from exiting. * So at least one thread must be alive to prevent the VM from terminating. * A UI application with any window displayed automatically satisfies this requirement. diff --git a/src/java.desktop/share/classes/javax/swing/AbstractButton.java b/src/java.desktop/share/classes/javax/swing/AbstractButton.java index ad5f0eba3de8..0361c5efc613 100644 --- a/src/java.desktop/share/classes/javax/swing/AbstractButton.java +++ b/src/java.desktop/share/classes/javax/swing/AbstractButton.java @@ -417,7 +417,7 @@ public void setMargin(Insets m) { * the label. * * @return an Insets object specifying the margin - * between the botton's border and the label + * between the button's border and the label * @see #setMargin */ public Insets getMargin() { diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java index 9a7e67913ffc..943bfc946d70 100644 --- a/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -3254,7 +3254,7 @@ public boolean getAutoscrolls() { * locations). If you do not wish for this component to respond in any way * to drops, you can disable drop support entirely either by removing the * drop target ({@code setDropTarget(null)}) or by de-activating it - * ({@code getDropTaget().setActive(false)}). + * ({@code getDropTarget().setActive(false)}). *

    * If the new {@code TransferHandler} is {@code null}, this method removes * the drop target. diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index fa8110d1517e..a13059f93b42 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -453,6 +453,14 @@ public enum PrintMode { * needed. */ private boolean columnSelectionAdjusting; + + /* + * True after column widths have been initialized/synchronized by layout. + * Used to distinguish the first preferred-width layout from later normal + * AUTO_RESIZE_LAST_COLUMN layouts. + */ + private boolean columnWidthsInitialized; + /** * The last value of getValueIsAdjusting from the row selection models * valueChanged notification. Used to test if a repaint is needed. @@ -1264,12 +1272,6 @@ public void setAutoResizeMode(int mode) { autoResizeMode = mode; resizeAndRepaint(); if (tableHeader != null) { - if (mode == JTable.AUTO_RESIZE_LAST_COLUMN) { - int colCnt = columnModel.getColumnCount(); - if (colCnt > 0) { - tableHeader.setResizingColumn(columnModel.getColumn(colCnt - 1)); - } - } tableHeader.resizeAndRepaint(); } firePropertyChange("autoResizeMode", old, autoResizeMode); @@ -3193,21 +3195,8 @@ private int viewIndexForColumn(TableColumn aColumn) { */ public void doLayout() { - boolean prefWidthSet = false; TableColumn resizingColumn = getResizingColumn(); - // doLayout is called for both pack and show - // so if initial preferred width is set by user then - // it needs to be honoured even if resizingColumn - // is set to last column on account of - // AUTO_RESIZE_LAST_COLUMN autoResizeMode - for (int i = 0; i < columnModel.getColumnCount(); i++) { - if (columnModel.getColumn(i).getPreferredWidth() != 75 - && columnModel.getColumn(i).getWidth() == 75) { - prefWidthSet = true; - break; - } - } - if (resizingColumn == null || prefWidthSet) { + if (resizingColumn == null) { setWidthsFromPreferredWidths(false); } else { @@ -3294,7 +3283,42 @@ public void sizeColumnsToFit(int resizingColumn) { } } + private void accommodateLastColumnOnly() { + int columnCount = getColumnCount(); + if (columnCount == 0) { + return; + } + + int delta = getWidth() - getColumnModel().getTotalColumnWidth(); + if (delta != 0) { + accommodateDelta(columnCount - 1, delta); + } + } + + private void setWidthsFromPreferredWidthsLastColumnOnly() { + int columnCount = getColumnCount(); + if (columnCount == 0) { + return; + } + + for (int i = 0; i < columnCount - 1; i++) { + TableColumn column = columnModel.getColumn(i); + column.setWidth(column.getPreferredWidth()); + } + + accommodateLastColumnOnly(); + } + private void setWidthsFromPreferredWidths(final boolean inverse) { + if (!inverse && autoResizeMode == AUTO_RESIZE_LAST_COLUMN) { + if (!columnWidthsInitialized) { + setWidthsFromPreferredWidthsLastColumnOnly(); + } else { + accommodateLastColumnOnly(); + } + columnWidthsInitialized = true; + return; + } int totalWidth = getWidth(); int totalPreferred = getPreferredSize().width; int target = !inverse ? totalWidth : totalPreferred; @@ -3323,6 +3347,7 @@ public void setSizeAt(int s, int i) { }; adjustSizes(target, r, inverse); + columnWidthsInitialized = true; } @@ -3821,6 +3846,7 @@ public void setColumnModel(final TableColumnModel columnModel) { if (columnModel == null) { throw new IllegalArgumentException("Cannot set a null ColumnModel"); } + columnWidthsInitialized = false; TableColumnModel old = this.columnModel; if (columnModel != old) { if (old != null) { @@ -4634,6 +4660,10 @@ private void tableRowsDeleted(TableModelEvent e) { * @see TableColumnModelListener */ public void columnAdded(TableColumnModelEvent e) { + if (columnWidthsInitialized) { + TableColumn column = columnModel.getColumn(e.getToIndex()); + column.setWidth(column.getPreferredWidth()); + } // If I'm currently editing, then I should stop editing if (isEditing()) { removeEditor(); diff --git a/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java b/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java index 8b65bfc962b4..db324fb676bd 100644 --- a/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java +++ b/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -119,6 +119,20 @@ public Set keySet() return set; } + @Override + public boolean containsKey(Object key) { + if (super.containsKey(key)) { + return true; + } + + for (UIDefaults table : tables) { + if (table != null && table.containsKey(key)) { + return true; + } + } + return false; + } + @Override public Enumeration elements() { diff --git a/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java b/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java index cea800d42f2c..96fa68fef944 100644 --- a/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java +++ b/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, 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 @@ -534,7 +534,7 @@ protected class AccessibleProgressMonitor extends AccessibleContext * AccessibleJLabel * AccessibleJProgressBar * - * The abstraction presented to assitive technologies by + * The abstraction presented to assistive technologies by * the AccessibleProgressMonitor is that a dialog contains a * progress monitor with three children: a message, a note * label and a progress bar. diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java index de3cb1adf752..34829b1ac58f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -532,7 +532,7 @@ protected LayoutManager createLayoutManager() { } /** - * Creates the default renderer that will be used in a non-editiable combo + * Creates the default renderer that will be used in a non-editable combo * box. A default renderer will used only if a renderer has not been * explicitly set with setRenderer. * diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java index 37bcbec21567..8eb7f11be3f7 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java @@ -747,7 +747,7 @@ InputMap getInputMap(int condition) { /** * Unregisters keyboard actions installed from * installKeyboardActions. - * This method is called at uninstallUI() time - subclassess should + * This method is called at uninstallUI() time - subclasses should * ensure that all of the keyboard actions registered at installUI * time are removed here. * diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java index 0c373483153d..821a31854179 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java @@ -105,7 +105,7 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements private ButtonHandler buttonHandler; /** - * Handler for repainting combo when editor component gains/looses focus + * Handler for repainting combo when editor component gains/loses focus */ private EditorFocusHandler editorFocusHandler; @@ -766,7 +766,7 @@ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {} } /** - * Handler for repainting combo when editor component gains/looses focus + * Handler for repainting combo when editor component gains/loses focus */ private static class EditorFocusHandler implements FocusListener, PropertyChangeListener { diff --git a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java index 64d2e0e3bbf8..5e4fef910170 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3164,7 +3164,7 @@ public ChangeListener[] getChangeListeners() { /** - * SelectorMapping contains a specifitiy, as an integer, and an associated + * SelectorMapping contains a specificity, as an integer, and an associated * Style. It can also reference children SelectorMappings, * so that it behaves like a tree. *

    diff --git a/src/java.desktop/share/classes/sun/awt/UngrabEvent.java b/src/java.desktop/share/classes/sun/awt/UngrabEvent.java index c9d97b8ed951..2c4ea174e826 100644 --- a/src/java.desktop/share/classes/sun/awt/UngrabEvent.java +++ b/src/java.desktop/share/classes/sun/awt/UngrabEvent.java @@ -30,7 +30,7 @@ /** * Sent when one of the following events occur on the grabbed window:

      - *
    • it looses focus, but not to one of the owned windows + *
    • it loses focus, but not to one of the owned windows *
    • mouse click on the outside area happens (except for one of the owned windows) *
    • switch to another application or desktop happens *
    • click in the non-client area of the owning window or this window happens diff --git a/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java b/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java index 66ccf7499c54..95657fbb5fd9 100644 --- a/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java +++ b/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java @@ -68,7 +68,7 @@ * The FontDesignMetrics class expresses font metrics in terms of arbitrary * typographic units (not points) chosen by the font supplier * and used in the underlying platform font representations. These units are - * defined by dividing the em-square into a grid. The em-sqaure is the + * defined by dividing the em-square into a grid. The em-square is the * theoretical square whose dimensions are the full body height of the * font. A typographic unit is the smallest measurable unit in the * em-square. The number of units-per-em is determined by the font diff --git a/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java b/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java index df67a40fd141..589e24327d56 100644 --- a/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java +++ b/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, 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 @@ -39,7 +39,7 @@ public class FontManagerNativeLibrary { top of freetype library (that is used in binary form). This wrapper is compiled into fontmanager and this make - fontmanger library depending on freetype library. + font manager library depending on freetype library. On Windows DLL's in the JRE's BIN directory cannot be found by windows DLL loading as that directory is not diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java index e7f3e6178fae..8652388edb16 100644 --- a/src/java.desktop/share/classes/sun/font/HBShaper.java +++ b/src/java.desktop/share/classes/sun/font/HBShaper.java @@ -387,7 +387,9 @@ private static int get_glyph_v_advance( */ private static class IntPtr { MemorySegment seg; + @SuppressWarnings("restricted") IntPtr(MemorySegment seg) { + this.seg = seg.reinterpret(4); } void set(int i) { diff --git a/src/java.desktop/share/classes/sun/font/SunFontManager.java b/src/java.desktop/share/classes/sun/font/SunFontManager.java index f9808530866a..ffd22f7aa42e 100644 --- a/src/java.desktop/share/classes/sun/font/SunFontManager.java +++ b/src/java.desktop/share/classes/sun/font/SunFontManager.java @@ -2617,7 +2617,7 @@ public boolean registerFont(Font font) { * - family name is not the same as the full name of an installed font * - full name is not the same as the family name of an installed font * The last two of these may initially look odd but the reason is - * that (unfortunately) Font constructors do not distinuguish these. + * that (unfortunately) Font constructors do not distinguish these. * An extreme example of such a problem would be a font which has * family name "Dialog.Plain" and full name of "Dialog". * The one arguably overly stringent restriction here is that if an diff --git a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java index 6b33f0786a8d..11586c67ff50 100644 --- a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java +++ b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java @@ -75,7 +75,7 @@ public class DesktopProperty implements UIDefaults.ActiveValue { /** - * Cleans up any lingering state held by unrefeernced + * Cleans up any lingering state held by unreferenced * DesktopProperties. */ public static void flushUnreferencedProperties() { diff --git a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c index ac37ad8eab6f..49520b45e312 100644 --- a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c +++ b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c @@ -519,7 +519,7 @@ static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte /* * Set up error handling to use setjmp/longjmp. This is the third such * setup, as both the AWT jpeg decoder and the com.sun... JPEG classes - * setup thier own. Ultimately these should be integrated, as they all + * setup their own. Ultimately these should be integrated, as they all * do pretty much the same thing. */ @@ -2358,7 +2358,7 @@ imageio_term_destination (j_compress_ptr cinfo) JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2); /* find out how much needs to be written */ - /* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */ + /* this conversion from size_t to jint is safe, because the length of the buffer is limited by jint */ jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer); if (datacount != 0) { diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java index 3a9855420588..558807403752 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java +++ b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,10 +31,10 @@ * Standard X Atom are defined by X11 and these atoms are defined in this class * for convenience. Common X Atoms like {@code XA_WM_NAME} are used to communicate with the * Window manager to let it know the Window name. The use and protocol for these - * atoms are defined in the Inter client communications converntions manual. + * atoms are defined in the Inter client communications conventions manual. * User specified XAtoms are defined by specifying a name that gets Interned * by the XServer and an {@code XAtom} object is returned. An {@code XAtom} can also be created - * by using a pre-exisiting atom like {@code XA_WM_CLASS}. A {@code display} has to be specified + * by using a pre-existing atom like {@code XA_WM_CLASS}. A {@code display} has to be specified * in order to create an {@code XAtom}.

      * * Once an {@code XAtom} instance is created, you can call get and set property methods to diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java index 357088262605..1e7bd76413c9 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java @@ -51,4 +51,29 @@ public abstract XMLSignatureInput engineResolveURI(ResourceResolverContext conte */ public abstract boolean engineCanResolveURI(ResourceResolverContext context); + /** + * Returns the scheme for a URI. + * + * @param uri the URI + * @return the scheme, or {@code null} if none + */ + protected static final String scheme(String uri) { + if (uri == null) { + return null; + } + char[] uriChars = uri.toCharArray(); + // Similar to java.net.URI::parse. Find ':' before any of '/', '?', + // or '#', and treat the characters before it as scheme. + for (int i = 0; i < uriChars.length; i++) { + if (uriChars[i] == '/' || uriChars[i] == '?' || uriChars[i] == '#') { + return null; + } + if (uriChars[i] == ':') { + // No validation on the output since we only care if it's + // empty or equal to specific values. + return uri.substring(0, i); + } + } + return null; + } } diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java index deda69e98b96..dafa851f3d0c 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java @@ -207,6 +207,8 @@ private URLConnection openConnection(URL url, ResourceResolverContext context) t */ @Override public boolean engineCanResolveURI(ResourceResolverContext context) { + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + if (context.uriToResolve == null) { LOG.debug("quick fail, uri == null"); return false; @@ -217,11 +219,15 @@ public boolean engineCanResolveURI(ResourceResolverContext context) { return false; } - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + String uriToResolveScheme = scheme(context.uriToResolve); - if (context.uriToResolve.startsWith("http:") || - context.uriToResolve.startsWith("https:") || - context.baseUri != null && (context.baseUri.startsWith("http:") || context.baseUri.startsWith("https:"))) { + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("http".equals(baseUriScheme) || "https".equals(baseUriScheme)) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; + } + } else if (uriToResolveScheme.equals("http") || uriToResolveScheme.equals("https")) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java index d3970a3ea694..2a96866cf8be 100644 --- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java +++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java @@ -72,20 +72,23 @@ public boolean engineCanResolveURI(ResourceResolverContext context) { return false; } - if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#' || - context.uriToResolve.startsWith("http:") || context.uriToResolve.startsWith("https:")) { + if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#') { return false; } - try { - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + + String uriToResolveScheme = scheme(context.uriToResolve); - if (context.uriToResolve.startsWith("file:") || context.baseUri.startsWith("file:")) { + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("file".equals(baseUriScheme)) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } - } catch (Exception e) { - LOG.debug(e.getMessage(), e); + } else if (uriToResolveScheme.equals("file")) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; } LOG.debug("But I can't"); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java index 0650884f3e4c..a9b06237f23d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java @@ -174,6 +174,7 @@ public static class KindSelector { public static final KindSelector MDL = new KindSelector(0x40); public static final KindSelector ERR = new KindSelector(0x7f); public static final KindSelector ASG = new KindSelector(0x84); + public static final KindSelector ASG_OP = new KindSelector(0x184); //common derived selectors public static final KindSelector TYP_PCK = of(TYP, PCK); @@ -182,14 +183,14 @@ public static class KindSelector { public static final KindSelector VAL_TYP = of(VAL, TYP); public static final KindSelector VAL_TYP_PCK = of(VAL, TYP, PCK); - private final byte data; + private final int data; private KindSelector(int data) { - this.data = (byte) data; + this.data = data; } public static KindSelector of(KindSelector... kindSelectors) { - byte newData = 0; + int newData = 0; for (KindSelector kindSel : kindSelectors) { newData |= kindSel.data; } @@ -205,7 +206,7 @@ public boolean contains(KindSelector other) { } public boolean isAssignment() { - return ASG.subset(this) && !VAL.subset(this); + return ASG.subset(this) && !ASG_OP.subset(this); } /** A set of KindName(s) representing a set of symbol's kinds. */ 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 e49ffa921281..b065bcc75e98 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 @@ -485,7 +485,7 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type, if (type.hasTag(TypeTag.ARRAY)) { ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos); } else if (type.hasTag(TypeTag.TYPEVAR)) { - ret = type.annotatedType(onlyTypeAnnotations); + ret = type.annotatedType(annotations); } else if (type.getKind() == TypeKind.UNION) { // There is a TypeKind, but no TypeTag. UnionClassType ut = (UnionClassType) type; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 5b83447ce486..a6d8d88e972a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -2552,7 +2552,7 @@ public IntersectionClassType makeIntersectionType(List bounds, boolean all syms.noSymbol); IntersectionClassType intersectionType = new IntersectionClassType(bounds, bc, allInterfaces); bc.type = intersectionType; - bc.erasure_field = (bounds.head.hasTag(TYPEVAR)) ? + bc.erasure_field = (bounds.head.hasTag(TYPEVAR)) || bounds.head.hasTag(ARRAY) ? syms.objectType : // error condition, recover erasure(firstExplicitBound); bc.members_field = WriteableScope.create(bc); @@ -3864,28 +3864,31 @@ public List union(List cl1, List cl2) { * Intersect two closures */ public List intersect(List cl1, List cl2) { - if (cl1 == cl2) - return cl1; - if (cl1.isEmpty() || cl2.isEmpty()) - return List.nil(); - if (cl1.head.tsym.precedes(cl2.head.tsym, this)) - return intersect(cl1.tail, cl2); - if (cl2.head.tsym.precedes(cl1.head.tsym, this)) - return intersect(cl1, cl2.tail); - if (isSameType(cl1.head, cl2.head)) - return intersect(cl1.tail, cl2.tail).prepend(cl1.head); - if (cl1.head.tsym == cl2.head.tsym && - cl1.head.hasTag(CLASS) && cl2.head.hasTag(CLASS)) { - if (cl1.head.isParameterized() && cl2.head.isParameterized()) { - Type merge = merge(cl1.head,cl2.head); - return intersect(cl1.tail, cl2.tail).prepend(merge); - } - if (cl1.head.isRaw() || cl2.head.isRaw()) - return intersect(cl1.tail, cl2.tail).prepend(erasure(cl1.head)); - } - return intersect(cl1.tail, cl2.tail); + return intersectHelper(cl1, cl2, new HashMap<>()); } // where + private List intersectHelper(List cl1, List cl2, Map mergeCache) { + if (cl1 == cl2) + return cl1; + if (cl1.isEmpty() || cl2.isEmpty()) + return List.nil(); + if (cl1.head.tsym.precedes(cl2.head.tsym, this)) + return intersectHelper(cl1.tail, cl2, mergeCache); + if (cl2.head.tsym.precedes(cl1.head.tsym, this)) + return intersectHelper(cl1, cl2.tail, mergeCache); + if (isSameType(cl1.head, cl2.head)) + return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(cl1.head); + if (cl1.head.tsym == cl2.head.tsym && + cl1.head.hasTag(CLASS) && cl2.head.hasTag(CLASS)) { + if (cl1.head.isParameterized() && cl2.head.isParameterized()) { + Type merge = merge(cl1.head,cl2.head, mergeCache); + return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(merge); + } + if (cl1.head.isRaw() || cl2.head.isRaw()) + return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(erasure(cl1.head)); + } + return intersectHelper(cl1.tail, cl2.tail, mergeCache); + } class TypePair { final Type t1; final Type t2; @@ -3923,8 +3926,11 @@ boolean sameTypeComparator(Type t, Type s) { } }; - Set mergeCache = new HashSet<>(); - private Type merge(Type c1, Type c2) { + private Type merge(Type c1, Type c2, Map mergeCache) { + TypePair pair = new TypePair(c1, c2); + Type cached = mergeCache.get(pair); + if (cached != null && cached != noType) return cached; + ClassType class1 = (ClassType) c1; List act1 = class1.getTypeArguments(); ClassType class2 = (ClassType) c2; @@ -3938,14 +3944,15 @@ private Type merge(Type c1, Type c2) { } else if (containsType(act2.head, act1.head)) { merged.append(act2.head); } else { - TypePair pair = new TypePair(c1, c2); Type m; - if (mergeCache.add(pair)) { - m = new WildcardType(lub(wildUpperBound(act1.head), - wildUpperBound(act2.head)), + if (mergeCache.get(pair) == null) { + mergeCache.put(pair, noType); + m = new WildcardType(lubHelper(mergeCache, + wildUpperBound(act1.head), + wildUpperBound(act2.head)), BoundKind.EXTENDS, syms.boundClass); - mergeCache.remove(pair); + mergeCache.remove(pair, noType); } else { m = new WildcardType(syms.objectType, BoundKind.UNBOUND, @@ -3960,8 +3967,14 @@ private Type merge(Type c1, Type c2) { Assert.check(act1.isEmpty() && act2.isEmpty() && typarams.isEmpty()); // There is no spec detailing how type annotations are to // be inherited. So set it to noAnnotations for now - return new ClassType(class1.getEnclosingType(), merged.toList(), - class1.tsym); + Type result = new ClassType(class1.getEnclosingType(), merged.toList(), + class1.tsym); + /* We need to store this result, to potentially avoid OOM errors that can be produced when many types + * that are equal but with different identity are created while determining the lub, in particular + * when F-bounded generic classes are present + */ + mergeCache.put(pair, result); + return result; } /** @@ -4020,7 +4033,7 @@ public List closureMin(List cl) { * not exist return null. */ public Type lub(List ts) { - return lub(ts.toArray(new Type[ts.length()])); + return lubHelper(new HashMap<>(), ts.toArray(new Type[ts.length()])); } /** @@ -4028,6 +4041,14 @@ public Type lub(List ts) { * does not exist return the type of null (bottom). */ public Type lub(Type... ts) { + return lubHelper(new HashMap<>(), ts); + } + + private Type lubHelper(Map mergeCache, List ts) { + return lubHelper(mergeCache, ts.toArray(new Type[ts.length()])); + } + + private Type lubHelper(Map mergeCache, Type... ts) { final int UNKNOWN_BOUND = 0; final int ARRAY_BOUND = 1; final int CLASS_BOUND = 2; @@ -4086,7 +4107,7 @@ public Type lub(Type... ts) { } } // lub(A[], B[]) is lub(A, B)[] - return new ArrayType(lub(elements), syms.arrayClass); + return new ArrayType(lubHelper(mergeCache, elements), syms.arrayClass); case CLASS_BOUND: // calculate lub(A, B) @@ -4105,7 +4126,7 @@ public Type lub(Type... ts) { for (int i = startIdx + 1 ; i < ts.length ; i++) { Type t = ts[i]; if (t.hasTag(CLASS) || t.hasTag(TYPEVAR)) - cl = intersect(cl, erasedSupertypes(t)); + cl = intersectHelper(cl, erasedSupertypes(t), mergeCache); } //step 2 - compute minimal erased candidate set (MEC) List mec = closureMin(cl); @@ -4116,7 +4137,7 @@ public Type lub(Type... ts) { List lci = firstSuperType != null ? List.of(firstSuperType) : List.nil(); for (int i = startIdx + 1 ; i < ts.length ; i++) { Type superType = asSuper(ts[i], erasedSupertype.tsym); - lci = intersect(lci, superType != null ? List.of(superType) : List.nil()); + lci = intersectHelper(lci, superType != null ? List.of(superType) : List.nil(), mergeCache); } candidates = candidates.appendList(lci); } @@ -4132,7 +4153,7 @@ public Type lub(Type... ts) { classes = classes.prepend(ts[i]); } // lub(A, B[]) is lub(A, arraySuperType) - return lub(classes); + return lubHelper(mergeCache, classes); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index f8b200ab3b98..5a32378a9311 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -180,7 +180,7 @@ protected Attr(Context context) { statInfo = new ResultInfo(KindSelector.NIL, Type.noType); varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType); - varAssignmentOpInfo = new ResultInfo(KindSelector.of(KindSelector.VAL, KindSelector.ASG), Type.noType); + varAssignmentOpInfo = new ResultInfo(KindSelector.ASG_OP, Type.noType); unknownExprInfo = new ResultInfo(KindSelector.VAL, Type.noType); methodAttrInfo = new MethodAttrInfo(); unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType); @@ -1965,7 +1965,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) { } public void visitSynchronized(JCSynchronized tree) { - boolean identityType = chk.checkIdentityType(tree.pos(), attribExpr(tree.lock, env)); + boolean identityType = chk.checkIdentityRefType(tree.pos(), attribExpr(tree.lock, env)); if (identityType && tree.lock.type != null && tree.lock.type.isValueBased()) { log.warning(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 9cc7e187d146..f42d7d5d5166 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -733,14 +733,15 @@ Type checkRefType(DiagnosticPosition pos, Type t) { t); } - /** Check that type is an identity type, i.e. not a value type. + /** Check that type is an identity reference type, i.e. a reference type and + * not a value type. * When not discernible statically, give it the benefit of doubt * and defer to runtime. * * @param pos Position to be used for error reporting. * @param t The type to be checked. */ - boolean checkIdentityType(DiagnosticPosition pos, Type t) { + boolean checkIdentityRefType(DiagnosticPosition pos, Type t) { if (t.hasTag(TYPEVAR)) { t = types.skipTypeVars(t, false); } @@ -748,12 +749,15 @@ boolean checkIdentityType(DiagnosticPosition pos, Type t) { IntersectionClassType ict = (IntersectionClassType)t; boolean result = true; for (Type component : ict.getExplicitComponents()) { - result &= checkIdentityType(pos, component); + result &= checkIdentityRefType(pos, component); } return result; } - if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) { - typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t); + if (!t.isReference() || (t.isValueClass() && !t.tsym.isAbstract())) { + Fragment required = + allowValueClasses ? Fragments.TypeReqIdentity + : Fragments.TypeReqRef; + typeTagError(pos, diags.fragment(required), t); return false; } return true; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 1fa6e7736f8d..afd3fed8757e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1144,10 +1144,11 @@ void writeCode(Code code) { } // counter for number of generic local variables - if (code.varDebugInfo && code.varBufferSize > 0) { + int lvtSize = code.getLVTSize(); + if (code.varDebugInfo && lvtSize > 0) { int nGenericVars = 0; int alenIdx = writeAttr(names.LocalVariableTable); - databuf.appendChar(code.getLVTSize()); + databuf.appendChar(lvtSize); for (int i=0; i VectorMask fromLong(VectorSpecies species, long bits) { * {@code ETYPE} value and the {@code ETYPE} value representing * {@code -1}, respectively. * - * @apiNote For the sake of static type checking, users may wish - * to check the resulting vector against the expected integral - * lane type or species. If the mask is for a float-point - * species, then the resulting vector will have the same shape and - * lane size, but an integral type. If the mask is for an - * integral species, the resulting vector will be of exactly that - * species. + * @apiNote The returned vector has the same species as this mask. + * For a floating-point species, a set mask lane is represented in + * the returned vector by the floating-point value {@code -1}, + * rather than by setting all bits of the corresponding vector lane. * * @return a vector representation of this mask * @see Vector#check(Class) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index 4d4eea0c3c4f..1fa75b4b69a1 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java @@ -533,7 +533,7 @@ static boolean opKind(Operator op, int bit) { public static final Binary SUB = binary("SUB", "-", VectorSupport.VECTOR_OP_SUB, VO_ALL); /** Produce {@code a*b}. */ public static final Associative MUL = assoc("MUL", "*", VectorSupport.VECTOR_OP_MUL, VO_ALL+VO_ASSOC); - /** Produce {@code a/b}. Floating only. */ + /** Produce {@code a/b}. */ public static final Binary DIV = binary("DIV", "/", VectorSupport.VECTOR_OP_DIV, VO_ALL| VO_SPECIAL); /** Produce {@code min(a,b)}. */ public static final Associative MIN = assoc("MIN", "min", VectorSupport.VECTOR_OP_MIN, VO_ALL+VO_ASSOC); diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java b/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java index 45904e20a99c..17987fd4d7d4 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, 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 @@ -26,7 +26,7 @@ package com.sun.jdi; /** - * Thrown to indicate that there is an inconistency in the debug + * Thrown to indicate that there is an inconsistency in the debug * information provided by the target VM. For example, this exception * is thrown if there is a type mismatch between a retrieved value's * runtime type and its declared type as reported by the target VM. diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java b/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java index bfe09a539cfa..0381e94469be 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, 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 @@ -54,7 +54,7 @@ public interface LaunchingConnector extends Connector { * received. *

      * Important note: If a target VM is launched through this - * funcctions, its output and error streams must be read as it + * function, its output and error streams must be read as it * executes. These streams are available through the * {@link java.lang.Process Process} object returned by * {@link VirtualMachine#process}. If the streams are not periodically diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java b/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java index 0fe7bed9c095..a8758df40558 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, 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 @@ -100,7 +100,7 @@ public Connection() {} * the connection while the readPacket is in progress. * * @throws java.io.IOException - * If the length of the packet (as indictaed by the first + * If the length of the packet (as indicated by the first * 4 bytes) is less than 11 bytes, or an I/O error occurs. * * diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java index 079f993c019f..b2bdf2d5e7e6 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, 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 @@ -105,7 +105,7 @@ public interface ClassPrepareRequest extends EventRequest { * refType.availableStrata(); * * such that a name on the list returned by - * refType.sourceNames(someStratam) + * refType.sourceNames(someStratum) * * matches 'sourceNamePattern'. * Regular expressions are limited diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java index a4bee836db2c..7c2af377e506 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java @@ -36,7 +36,7 @@ /** * Request for notification of a thread in the target VM * attempting to enter a monitor already acquired by another thread. - * When an enabled MonitorContededEnterRequest is satisfied, an + * When an enabled MonitorContendedEnterRequest is satisfied, an * {@link EventSet event set} containing a * {@link MonitorContendedEnterEvent MonitorContendedEnterEvent} * will be placed on the {@link EventQueue EventQueue}. diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java index b9f55df95506..ff24d8e8937a 100644 --- a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java +++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java @@ -36,7 +36,7 @@ /** * Request for notification of a thread in the target VM entering a monitor * after waiting for it to be released by another thread. - * When an enabled MonitorContededEnteredRequest is satisfied, an + * When an enabled MonitorContendedEnteredRequest is satisfied, an * {@link EventSet event set} containing a * {@link MonitorContendedEnteredEvent MonitorContendedEnteredEvent} * will be placed on the {@link EventQueue EventQueue}. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java index 83d8b8c5f17d..a60117fe316c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.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 @@ -167,10 +167,11 @@ private byte[] readWithHeader(int size) throws IOException { byte[] bytes = new byte[Math.max(HEADER_SIZE, size)]; for (int attempts = 0; attempts < 25; attempts++) { // read twice and check files state to avoid simultaneous change by JVM - input.position(0); - input.readFully(bytes, 0, HEADER_SIZE); - input.position(0); - input.readFully(headerBytes); + input.positionPhysical(0); + input.readPhysicalFully(bytes, 0, HEADER_SIZE); + input.positionPhysical(0); + input.readPhysicalFully(headerBytes, 0, HEADER_SIZE); + input.position(HEADER_SIZE); if (bytes[HEADER_FILE_STATE_POSITION] != MODIFYING_STATE) { if (bytes[HEADER_FILE_STATE_POSITION] == headerBytes[HEADER_FILE_STATE_POSITION]) { ByteBuffer buffer = ByteBuffer.wrap(bytes); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java index 33cb928bbbfd..001bd39896e3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -110,6 +110,10 @@ long readPhysicalLong() throws IOException { return file.readLong(); } + void readPhysicalFully(byte[] dest, int offset, int length) throws IOException { + file.readFully(dest, offset, length); + } + @Override public final byte readByte() throws IOException { if (!currentBlock.contains(position)) { diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java index 8fdd4e31bd9e..9440f15c02fe 100644 --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, 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 @@ -142,7 +142,7 @@ public static MessageInfo createOutgoing(Association association, * otherwise the preferred destination of the message to be sent. * * @return The socket address, or {@code null} if this instance is to be - * used for sending a message and has been construced without + * used for sending a message and has been constructed without * specifying a preferred destination address * */ @@ -154,7 +154,7 @@ public static MessageInfo createOutgoing(Association association, * sent on. * * @return The association, or {@code null} if this instance is to be - * used for sending a message and has been construced using the + * used for sending a message and has been constructed using the * the {@link #createOutgoing(SocketAddress,int) * createOutgoing(SocketAddress,int)} static factory method */ @@ -163,7 +163,7 @@ public static MessageInfo createOutgoing(Association association, /** * Returns the number of bytes read for the received message. * - *

      This method is only appicable for received messages, it has no + *

      This method is only applicable for received messages, it has no * meaning for messages being sent. * * @return The number of bytes read, {@code -1} if the channel is an {@link diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java index 312a143a8e43..9e38f5fb7bda 100644 --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.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 @@ -438,7 +438,7 @@ public abstract SctpChannel unbindAddress(InetAddress address) /** * Connects this channel's socket. * - *

      This is a convience method and is equivalent to evaluating the + *

      This is a convenience method and is equivalent to evaluating the * following expression: *

            * setOption(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
      @@ -723,7 +723,7 @@ public final int validOps() {
            * does not contain the complete message, then an invocation of {@link
            * MessageInfo#isComplete isComplete} on the returned {@code
            * MessageInfo} will return {@code false}, and more invocations of this
      -     * method will be necessary to completely consume the messgae. Only
      +     * method will be necessary to completely consume the message. Only
            * one message at a time will be partially delivered in any stream. The
            * socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
            * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
      @@ -829,7 +829,7 @@ public abstract  MessageInfo receive(ByteBuffer dst,
            *          output buffer
            *
            * @throws  InvalidStreamException
      -     *          If {@code streamNumner} is negative or greater than or equal to
      +     *          If {@code streamNumber} is negative or greater than or equal to
            *          the maximum number of outgoing streams
            *
            * @throws  java.nio.channels.ClosedChannelException
      diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
      index 54b5bb796b06..d877b5a5fefb 100644
      --- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
      +++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.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
      @@ -544,7 +544,7 @@ public final int validOps() {
            * contain the complete message, then an invocation of {@link
            * MessageInfo#isComplete isComplete} on the returned {@code
            * MessageInfo} will return {@code false}, and more invocations of this
      -     * method will be necessary to completely consume the messgae. Only
      +     * method will be necessary to completely consume the message. Only
            * one message at a time will be partially delivered in any stream. The
            * socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
            * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
      diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt
      index e25d3d644a17..53bb850a6ecc 100644
      --- a/test/hotspot/jtreg/ProblemList-Virtual.txt
      +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt
      @@ -60,6 +60,9 @@ vmTestbase/nsk/jdb/where/where005/where005.java 8278470 generic-all
       
       vmTestbase/nsk/jdb/list/list003/list003.java        8300707 generic-all
       vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java  8300707 generic-all
      +###
      +# suspend001 hangs with virtual threads until the suspended-successor issue is fixed.
      +vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java 8390031 generic-all
       
       ####
       ## NSK JDI tests failing with wrapper
      diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt
      index 1262c1698bd6..c6443fd3f932 100644
      --- a/test/hotspot/jtreg/ProblemList-Xcomp.txt
      +++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt
      @@ -62,5 +62,3 @@ gc/arguments/TestNewSizeFlags.java 8299116 macosx-aarch64
       #############################################################################
       
       # Value Objects failures start here:
      -runtime/cds/appcds/aotCache/AOTMapTest.java#valhalla 8388438 generic-all
      -runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java 8388438 generic-all
      diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
      index ccc9a9dac567..3e73792dd7df 100644
      --- a/test/hotspot/jtreg/ProblemList.txt
      +++ b/test/hotspot/jtreg/ProblemList.txt
      @@ -87,11 +87,22 @@ gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#generational 83869
       gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#default 8386964 generic-all
       gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all
       gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8386964 generic-all
      +gc/TestGCALotAtAllSafepoints.java#Parallel 8390661 generic-all
      +gc/TestGCALotAtAllSafepoints.java#Serial 8390661 generic-all
      +gc/TestGCALotAtAllSafepoints.java#G1 8390661 generic-all
      +gc/TestGCALotAtAllSafepoints.java#Z 8390661 generic-all
      +gc/TestGCALotAtAllSafepoints.java#Shenandoah 8390661 generic-all
       
       #############################################################################
       
       # :hotspot_runtime
       
      +runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java 8390485 generic-all
      +runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java 8390485 generic-all
      +runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java 8390485 generic-all
      +runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java 8390485 generic-all
      +runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java 8390485 generic-all
      +runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java 8390485 generic-all
       runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64
       runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x
       runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64
      @@ -204,8 +215,6 @@ compiler/vectorapi/TestVectorReassociations.java 8388927 generic-all
       
       serviceability/sa/TestJhsdbJstackMixedWithXComp.java#xcomp-disable-tiered-compilation 8386674 linux-aarch64
       
      -vmTestbase/nsk/jvmti/IterateOverReachableObjects/iterreachobj002/TestDescription.java 8384882 linux-all
      -
       # The following test failure(s) DID NOT reproduce during Tier[1-8] testing
       # done for 8377828. Further analysis was done with 8376235 and just one
       # sub-test needed to remain on the ProblemList via 8361089.
      diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
      index b7dcba10a353..0216f5f13550 100644
      --- a/test/hotspot/jtreg/TEST.groups
      +++ b/test/hotspot/jtreg/TEST.groups
      @@ -188,7 +188,7 @@ tier1_compiler_1 = \
         -compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java \
         -compiler/codecache/stress \
         -compiler/codegen/aes
      -  
      +
       tier1_compiler_2 = \
         compiler/gcbarriers/ \
         compiler/igvn/ \
      @@ -442,9 +442,9 @@ hotspot_cds_only = \
       
       hotspot_appcds_dynamic = \
         runtime/cds/appcds/ \
      + -runtime/cds/appcds/agent \
        -runtime/cds/appcds/aotAnnotations \
        -runtime/cds/appcds/aotCache \
      - -runtime/cds/appcds/aotClassLinking \
        -runtime/cds/appcds/aotCode \
        -runtime/cds/appcds/aotFlags \
        -runtime/cds/appcds/aotProfile \
      @@ -539,80 +539,6 @@ hotspot_cds_epsilongc = \
         runtime/cds/appcds/jigsaw \
         runtime/cds/appcds/loaderConstraints
       
      -# Run "old" CDS tests with -XX:+AOTClassLinking. This should include most CDS tests, except for
      -# those that rely on redefining classes that are already archived.
      -# Note that appcds/aotXXX directories are excluded -- those tests already specifically
      -# test AOT class linking, so there's no need to run them again with -XX:+AOTClassLinking.
      -hotspot_aot_classlinking = \
      -  runtime/cds \
      - -runtime/cds/appcds/aotAnnotations \
      - -runtime/cds/appcds/aotCache \
      - -runtime/cds/appcds/aotClassLinking \
      - -runtime/cds/appcds/aotCode \
      - -runtime/cds/appcds/aotFlags \
      - -runtime/cds/appcds/aotProfile \
      - -runtime/cds/appcds/ArchivedFieldMetadataMismatchTest.java \
      - -runtime/cds/appcds/BadBSM.java \
      - -runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java \
      - -runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java \
      - -runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
      - -runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
      - -runtime/cds/appcds/CDSandJFR.java \
      - -runtime/cds/appcds/LambdaContainsOldInf.java \
      - -runtime/cds/appcds/customLoader/CustomClassListDump.java \
      - -runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
      - -runtime/cds/appcds/customLoader/OldClassAndInf.java \
      - -runtime/cds/appcds/customLoader/ParallelTestMultiFP.java \
      - -runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
      - -runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
      - -runtime/cds/appcds/DumpClassListWithLF.java \
      - -runtime/cds/appcds/dynamicArchive \
      - -runtime/cds/appcds/HelloExtTest.java \
      - -runtime/cds/appcds/InlineFieldExclusionTest.java \
      - -runtime/cds/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java \
      - -runtime/cds/appcds/jigsaw/ExactOptionMatch.java \
      - -runtime/cds/appcds/jigsaw/JigsawOptionsCombo.java \
      - -runtime/cds/appcds/jigsaw/modulepath/AddModules.java \
      - -runtime/cds/appcds/jigsaw/modulepath/JvmtiAddPath.java \
      - -runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java \
      - -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java \
      - -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java \
      - -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java \
      - -runtime/cds/appcds/jigsaw/overridetests/OverrideTests.java \
      - -runtime/cds/appcds/jigsaw/RedefineClassesInModuleGraph.java \
      - -runtime/cds/appcds/JvmtiAddPath.java \
      - -runtime/cds/appcds/jvmti \
      - -runtime/cds/appcds/LambdaProxyClasslist.java \
      - -runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java \
      - -runtime/cds/appcds/methodHandles \
      - -runtime/cds/appcds/NestHostOldInf.java \
      - -runtime/cds/appcds/OldClassTest.java \
      - -runtime/cds/appcds/OldClassWithjsr.java \
      - -runtime/cds/appcds/OldInfExtendsInfDefMeth.java \
      - -runtime/cds/appcds/OldSuperClass.java \
      - -runtime/cds/appcds/OldSuperInfIndirect.java \
      - -runtime/cds/appcds/OldSuperInf.java \
      - -runtime/cds/appcds/redefineClass \
      - -runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java \
      - -runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java \
      - -runtime/cds/appcds/resolvedConstants/ResolvedConstants.java \
      - -runtime/cds/appcds/RewriteBytecodesInlineTest.java \
      - -runtime/cds/appcds/RewriteBytecodesTest.java \
      - -runtime/cds/appcds/SignedJar.java \
      - -runtime/cds/appcds/SpecifySysLoaderProp.java \
      - -runtime/cds/appcds/StaticArchiveWithLambda.java \
      - -runtime/cds/appcds/TestEpsilonGCWithCDS.java \
      - -runtime/cds/appcds/TestParallelGCWithCDS.java \
      - -runtime/cds/appcds/TestSerialGCWithCDS.java \
      - -runtime/cds/appcds/TestZGCWithCDS.java \
      - -runtime/cds/appcds/TestWithProfiler.java \
      - -runtime/cds/appcds/VerifyObjArrayCloneTest.java \
      - -runtime/cds/serviceability/ReplaceCriticalClassesForSubgraphs.java \
      - -runtime/cds/serviceability/ReplaceCriticalClasses.java \
      - -runtime/cds/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java \
      - -runtime/cds/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java \
      - -runtime/cds/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java
      -
       # needs -nativepath:/images/test/hotspot/jtreg/native/
       hotspot_metaspace = \
         gtest/MetaspaceGtests.java \
      diff --git a/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java b/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
      index 8bf8c9846ec7..53f6b28906b3 100644
      --- a/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
      +++ b/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
      @@ -23,7 +23,7 @@
       
       /**
        * @test
      - * @bug 7200264
      + * @bug 7200264 8390044
        * @summary 7192963 changes disabled shift vectors
        * @library /test/lib /
        * @run driver compiler.c2.cr7200264.TestIntVect
      @@ -555,16 +555,16 @@ void test_divc_n(int[] a0, int[] a1) {
               }
           }
       
      -    // Not vectorized: On AArch64 SVE, vectorization for this example results in
      -    // DivVI nodes.
      +    // Vectorization for this example results in DivVI nodes on AArch64 SVE and
      +    // RISC-V RVV.
           @Test
           @IR(counts = { IRNode.LOAD_VECTOR_I, "> 0",
                          IRNode.STORE_VECTOR,  "> 0",
                          IRNode.DIV_VI,        "> 0" },
      -        applyIfCPUFeature = {"sve", "true"})
      +        applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
           @IR(counts = { IRNode.LOAD_VECTOR_I, "= 0",
                          IRNode.STORE_VECTOR,  "= 0" },
      -        applyIfCPUFeature = {"sve", "false"})
      +        applyIfCPUFeatureAnd = {"sve", "false", "rvv", "false"})
           void test_divv(int[] a0, int[] a1, int b) {
               for (int i = 0; i < a0.length; i+=1) {
                   a0[i] = (int)(a1[i]/b);
      diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java
      new file mode 100644
      index 000000000000..8bb76c6155f6
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java
      @@ -0,0 +1,295 @@
      +/*
      + * 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.c2.igvn;
      +
      +import compiler.lib.generators.*;
      +import compiler.lib.ir_framework.*;
      +import jdk.test.lib.Asserts;
      +
      +/*
      + * @test
      + * @bug 8389579
      + * @key randomness
      + * @summary Test missing Ideal optimization opportunity for CompressBits and ExpandBits.
      + * @library /test/lib /
      + * @run driver ${test.main.class}
      + */
      +
      +public class TestMissingCompressBitsAndExpandBitsIdeal {
      +    private static final Generator INTS = Generators.G.ints();
      +    private static final Generator LONGS = Generators.G.longs();
      +
      +    private static final RestrictableGenerator INT_SHIFTS = Generators.G.ints()
      +                                                                               .restricted(1, Integer.SIZE - 1);
      +    private static final RestrictableGenerator LONG_SHIFTS = Generators.G.ints()
      +                                                                                .restricted(1, Long.SIZE - 1);
      +
      +    public static void main(String[] args) {
      +        TestFramework.runWithFlags("-XX:+IgnoreUnrecognizedVMOptions",
      +                                   "-XX:VerifyIterativeGVN=100");
      +    }
      +
      +    // compress(x, 1 << n) == (x >> n) & 1
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntCompressWithOneLeftShift(int value, int shift) {
      +        int i;
      +        for (i = -10; i < 1; i++) {
      +        }
      +        int mask = i << shift;
      +        return Integer.compress(value, mask);
      +    }
      +
      +    // compress(x, -1 << n) == x >>> n
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntCompressWithMinusOneLeftShift(int value, int shift) {
      +        int i;
      +        for (i = -10; i < -1; i++) {
      +        }
      +        int mask = i << shift;
      +        return Integer.compress(value, mask);
      +    }
      +
      +    // compress(x, 1L << n) == (x >> n) & 1L
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongCompressWithOneLeftShift(long value, int shift) {
      +        long i;
      +        for (i = -10; i < 1; i++) {
      +        }
      +        long mask = i << shift;
      +        return Long.compress(value, mask);
      +    }
      +
      +    // compress(x, -1L << n) == x >>> n
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongCompressWithMinusOneLeftShift(long value, int shift) {
      +        long i;
      +        for (i = -10; i < -1; i++) {
      +        }
      +        long mask = i << shift;
      +        return Long.compress(value, mask);
      +    }
      +
      +    // compress(expand(x, m), m) == x & compress(m, m)
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0",
      +                  IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntCompressExpandWithSameMask(int value, int mask) {
      +        int i;
      +        for (i = -10; i < 1; i++) {
      +        }
      +        int expandMask = mask * i;
      +        return Integer.compress(Integer.expand(value, expandMask), mask);
      +    }
      +
      +    // compress(expand(x, m), m) == x & compress(m, m)
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0",
      +                  IRNode.COMPRESS_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongCompressExpandWithSameMask(long value, long mask) {
      +        long i;
      +        for (i = -10; i < 1; i++) {
      +        }
      +        long expandMask = mask * i;
      +        return Long.compress(Long.expand(value, expandMask), mask);
      +    }
      +
      +    // expand(x, 1 << n) == (x & 1) << n
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntExpandWithOneLeftShift(int value, int shift) {
      +        int result = 0;
      +        for (int i = 1; i >= 1; i--) {
      +            int x = value;
      +            result = Integer.expand(x, i << shift);
      +        }
      +        return result;
      +    }
      +
      +    // expand(x, -1 << n) == x << n
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntExpandWithMinusOneLeftShift(int value, int shift) {
      +        int result = 0;
      +        for (int i = -1; i >= -1; i--) {
      +            int x = value;
      +            result = Integer.expand(x, i << shift);
      +        }
      +        return result;
      +    }
      +
      +    // expand(x, 1L << n) == (x & 1L) << n
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongExpandWithOneLeftShift(long value, int shift) {
      +        long result = 0;
      +        for (long i = 1L; i >= 1L; i--) {
      +            long x = value;
      +            result = Long.expand(x, i << shift);
      +        }
      +        return result;
      +    }
      +
      +    // expand(x, -1L << n) == x << n
      +    @Test
      +    @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongExpandWithMinusOneLeftShift(long value, int shift) {
      +        long result = 0;
      +        for (long i = -1L; i >= -1L; i--) {
      +            long x = value;
      +            result = Long.expand(x, i << shift);
      +        }
      +        return result;
      +    }
      +
      +    // expand(compress(x, m), m) == x & m
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0",
      +                  IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS, IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static int testIntExpandCompressWithSameMask(int value, int mask) {
      +        int result = 0;
      +        for (int i = 1; i >= 1; i--) {
      +            int x = value;
      +            result = Integer.expand(Integer.compress(x, mask * i), mask);
      +        }
      +        return result;
      +    }
      +
      +    // expand(compress(x, m), m) == x & m
      +    @Test
      +    @IR(counts = {IRNode.COMPRESS_BITS, "> 0",
      +                  IRNode.EXPAND_BITS, "> 0"},
      +        phase = {CompilePhase.AFTER_PARSING},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    @IR(failOn = {IRNode.EXPAND_BITS, IRNode.COMPRESS_BITS},
      +        applyIfCPUFeature = {"bmi2", "true"})
      +    public static long testLongExpandCompressWithSameMask(long value, long mask) {
      +        long result = 0;
      +        for (long i = 1L; i >= 1L; i--) {
      +            long x = value;
      +            result = Long.expand(Long.compress(x, mask * i), mask);
      +        }
      +        return result;
      +    }
      +
      +    @Run(test = {"testIntCompressWithOneLeftShift",
      +                 "testIntCompressWithMinusOneLeftShift",
      +                 "testLongCompressWithOneLeftShift",
      +                 "testLongCompressWithMinusOneLeftShift",
      +                 "testIntCompressExpandWithSameMask",
      +                 "testLongCompressExpandWithSameMask",
      +                 "testIntExpandWithOneLeftShift",
      +                 "testIntExpandWithMinusOneLeftShift",
      +                 "testLongExpandWithOneLeftShift",
      +                 "testLongExpandWithMinusOneLeftShift",
      +                 "testIntExpandCompressWithSameMask",
      +                 "testLongExpandCompressWithSameMask"})
      +    public static void runTest() {
      +        for (int i = 0; i < 100; i++) {
      +            int intValue = INTS.next();
      +            int intShift = INT_SHIFTS.next();
      +            int intMask = INTS.next();
      +            long longValue = LONGS.next();
      +            int longShift = LONG_SHIFTS.next();
      +            long longMask = LONGS.next();
      +
      +            Asserts.assertEQ(testIntCompressWithOneLeftShift(intValue, intShift),
      +                             (intValue >> intShift) & 1);
      +            Asserts.assertEQ(testIntCompressWithMinusOneLeftShift(intValue, intShift),
      +                             intValue >>> intShift);
      +
      +            Asserts.assertEQ(testLongCompressWithOneLeftShift(longValue, longShift),
      +                             (longValue >> longShift) & 1L);
      +            Asserts.assertEQ(testLongCompressWithMinusOneLeftShift(longValue, longShift),
      +                             longValue >>> longShift);
      +
      +            Asserts.assertEQ(testIntCompressExpandWithSameMask(intValue, intMask),
      +                             intValue & Integer.compress(intMask, intMask));
      +            Asserts.assertEQ(testLongCompressExpandWithSameMask(longValue, longMask),
      +                             longValue & Long.compress(longMask, longMask));
      +
      +            Asserts.assertEQ(testIntExpandWithOneLeftShift(intValue, intShift),
      +                             (intValue & 1) << intShift);
      +            Asserts.assertEQ(testIntExpandWithMinusOneLeftShift(intValue, intShift),
      +                             intValue << intShift);
      +
      +            Asserts.assertEQ(testLongExpandWithOneLeftShift(longValue, longShift),
      +                             (longValue & 1L) << longShift);
      +            Asserts.assertEQ(testLongExpandWithMinusOneLeftShift(longValue, longShift),
      +                             longValue << longShift);
      +
      +            Asserts.assertEQ(testIntExpandCompressWithSameMask(intValue, intMask),
      +                             intValue & intMask);
      +            Asserts.assertEQ(testLongExpandCompressWithSameMask(longValue, longMask),
      +                             longValue & longMask);
      +        }
      +    }
      +}
      diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
      similarity index 92%
      rename from test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
      index 605f65eb00c0..55b07f1347a6 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
      @@ -21,7 +21,7 @@
        * questions.
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       /*
        * @test
      @@ -29,12 +29,12 @@
        * @summary Test case where we had escape analysis tell us that we can possibly eliminate
        *          the array allocation, then MergeStores introduces a mismatched store, which
        *          the actual elimination does not verify for. That led to wrong results.
      - * @run main/othervm -XX:CompileCommand=compileonly,compiler.c2.TestMergeStoresAndAllocationElimination::test
      - *                   -XX:CompileCommand=exclude,compiler.c2.TestMergeStoresAndAllocationElimination::dontinline
      + * @run main/othervm -XX:CompileCommand=compileonly,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::test
      + *                   -XX:CompileCommand=exclude,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::dontinline
        *                   -XX:-TieredCompilation -Xbatch
        *                   -XX:+IgnoreUnrecognizedVMOptions -XX:-CICompileOSR
      - *                   compiler.c2.TestMergeStoresAndAllocationElimination
      - * @run main compiler.c2.TestMergeStoresAndAllocationElimination
      + *                   compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
      + * @run main compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
        */
       
       public class TestMergeStoresAndAllocationElimination {
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
      similarity index 91%
      rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
      index f65643f8d870..17f92f593908 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
      @@ -27,10 +27,10 @@
        * @summary Check that the JVM is able to dump the heap even when there are ReduceAllocationMerge in the scope.
        * @library /test/lib /
        * @requires vm.flavor == "server"
      - * @run main/othervm compiler.c2.TestReduceAllocationAndHeapDump
      + * @run main/othervm compiler.escapeAnalysis.TestReduceAllocationAndHeapDump
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       import java.io.File;
       import jdk.test.lib.process.OutputAnalyzer;
      @@ -49,8 +49,8 @@ public static void main(String[] args) throws Exception {
                       "-XX:CompileThresholdScaling=0.01",
                       "-XX:+HeapDumpAfterFullGC",
                       "-XX:HeapDumpPath=" + dumpDirectory.getAbsolutePath(),
      -                "-XX:CompileCommand=compileonly,compiler.c2.HeapDumper::testIt",
      -                "-XX:CompileCommand=exclude,compiler.c2.HeapDumper::dummy",
      +                "-XX:CompileCommand=compileonly,compiler.escapeAnalysis.HeapDumper::testIt",
      +                "-XX:CompileCommand=exclude,compiler.escapeAnalysis.HeapDumper::dummy",
                       HeapDumper.class.getName()
                   };
       
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
      similarity index 95%
      rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
      index 765dcee7c5b3..740ce5792cef 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
      @@ -29,10 +29,10 @@
        * @requires vm.compiler2.enabled
        * @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndMemoryLoop*::test*
        *                   -XX:-TieredCompilation -Xbatch
      - *                   compiler.c2.TestReduceAllocationAndMemoryLoop
      + *                   compiler.escapeAnalysis.TestReduceAllocationAndMemoryLoop
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       public class TestReduceAllocationAndMemoryLoop {
           public static void main(String[] args) throws Exception {
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
      similarity index 95%
      rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
      index ccb00c635c52..5043e68f7fe0 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
      @@ -35,10 +35,10 @@
        *                   -XX:-TieredCompilation
        *                   -Xbatch
        *                   -Xcomp
      - *                   compiler.c2.TestReduceAllocationAndNonExactAllocate
      + *                   compiler.escapeAnalysis.TestReduceAllocationAndNonExactAllocate
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       import jdk.internal.misc.Unsafe;
       
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
      similarity index 95%
      rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
      index e5df54994499..353e1d67b2b5 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
      @@ -31,10 +31,10 @@
        * @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndNullableLoads*::*
        *                   -XX:CompileCommand=dontinline,*TestReduceAllocationAndNullableLoads*::*
        *                   -XX:-TieredCompilation -Xcomp
      - *                   compiler.c2.TestReduceAllocationAndNullableLoads
      + *                   compiler.escapeAnalysis.TestReduceAllocationAndNullableLoads
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       public class TestReduceAllocationAndNullableLoads {
           public static void main(String[] args) {
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
      similarity index 91%
      rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
      index e6309b50f56e..e829a05a7243 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
      @@ -29,11 +29,11 @@
        * @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndPointerComparisons*::*
        *                   -XX:CompileCommand=dontinline,*TestReduceAllocationAndPointerComparisons*::*
        *                   -XX:-TieredCompilation -Xcomp
      - *                   compiler.c2.TestReduceAllocationAndPointerComparisons
      - * @run main compiler.c2.TestReduceAllocationAndPointerComparisons
      + *                   compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
      + * @run main compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       public class TestReduceAllocationAndPointerComparisons {
           public static void main(String[] args) {
      diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java
      new file mode 100644
      index 000000000000..25660912fac7
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java
      @@ -0,0 +1,92 @@
      +/*
      + * 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 8389873
      + * @summary Check that Reduce Allocation Merges correctly handle the situation
      + *          where the Phi was optimized out by IGVN during CG construction.
      + * @run main/othervm -XX:CompileCommand=compileonly,*${test.main.class}*::*
      + *                   -Xcomp -XX:-TieredCompilation ${test.main.class}
      + * @run main ${test.main.class}
      + */
      +
      +package compiler.escapeAnalysis;
      +
      +public class TestReduceAllocationOptimizedOutPhi {
      +    static int var_369;
      +
      +    public static void main(String[] args) {
      +        for (int i = 0; i < 100; i++) {
      +            test();
      +        }
      +    }
      +
      +    static void test() {
      +        switch (new Foo().var_2) {
      +            case 3:
      +                var_369 = (1.0002043F == new Foo().var_2 ? new Bar() : new Bar()).var_151;
      +                var_369 = 4;
      +        }
      +        for (short var_480 = 0; var_480 < 1; var_480++) {
      +            var_369 = new Bar().var_151;
      +        }
      +    }
      +}
      +
      +class Foo {
      +    long var_1 = 9;
      +    char var_2 = 'B';
      +
      +    Foo() {
      +        int var_121 = 9;
      +        for (var_121 = 9; var_121 >= 0; var_121--) {
      +            double var_122 = 2.2250738585072014E-308 - (Integer) 0;
      +            float var_123 = '@' * (Integer) (Byte.valueOf((byte) 9) * var_121);
      +        }
      +        long var_124 = (Float.intBitsToFloat(608) == '(' ? 7L : 5);
      +        byte var_127 = 5;
      +        for (var_127 = 55; var_127 >= 0; var_127--) {
      +            if (370 <= -Byte.valueOf((byte) 14)) {
      +                long var_131 = 71L + (Integer) (-Character.valueOf('d'));
      +                ++var_131;
      +                var_131--;
      +                var_131 >>>= var_131;
      +            }
      +        }
      +        byte var_132 = 0;
      +        for (var_132 = 0; var_132 < 6; var_132++) {
      +            byte var_134 = ++var_127;
      +            short var_135 = 2046;
      +            double var_136 = -var_132 < 0.18568599F ? 0.44624205067529954 * Long.valueOf(1048575) + '~' : Short.valueOf((short) 4085);
      +            byte var_137 = 3;
      +            double var_138 = -Integer.valueOf(6) - 0.9994681372166424 * Long.valueOf(3) + (Integer) (+Short.valueOf((short) 512));
      +            int var_141 = (Integer) (Byte.valueOf((byte) 7) * Character.valueOf('U')) - 2;
      +        }
      +    }
      +}
      +
      +class Bar {
      +    Byte var_151 = 3;
      +}
      +
      diff --git a/test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
      similarity index 96%
      rename from test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
      index 1247effd46d3..f56881ef3fbf 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
      @@ -26,10 +26,10 @@
        * @bug 8361140
        * @summary Test ConnectionGraph::reduce_phi_on_cmp when OptimizePtrCompare is disabled
        * @library /test/lib /
      - * @run driver compiler.c2.TestReducePhiOnCmpWithNoOptPtrCompare
      + * @run driver compiler.escapeAnalysis.TestReducePhiOnCmpWithNoOptPtrCompare
        */
       
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       import java.util.Random;
       import jdk.test.lib.Asserts;
      @@ -87,4 +87,4 @@ public boolean equals(Object o) {
                   return (p.x == x) && (p.y == y);
               }
           }
      -}
      \ No newline at end of file
      +}
      diff --git a/test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
      similarity index 95%
      rename from test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java
      rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
      index b5e349307c36..56f74d2285b3 100644
      --- a/test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java
      +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
      @@ -28,10 +28,10 @@
        * @library /test/lib /
        * @requires vm.debug & vm.compiler2.enabled
        * @compile -XDstringConcat=inline TestScalarReplacementMaxLiveNodes.java
      - * @run main/othervm/timeout=480 compiler.c2.TestScalarReplacementMaxLiveNodes
      + * @run main/othervm/timeout=480 compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
        * @run main/othervm/timeout=480 -Xbatch -XX:-OptimizeStringConcat -XX:-TieredCompilation
        *                   -XX:+UnlockDiagnosticVMOptions -XX:+ReduceAllocationMerges
      - *                   -XX:CompileCommand=dontinline,compiler.c2.TestScalarReplacementMaxLiveNodes::test
      + *                   -XX:CompileCommand=dontinline,compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes::test
        *                   -XX:CompileCommand=compileonly,*TestScalarReplacementMaxLiveNodes*::*test*
        *                   -XX:CompileCommand=inline,*String*::*
        *                   -XX:CompileCommand=dontinline,*StringBuilder*::ensureCapacityInternal
      @@ -39,9 +39,9 @@
        *                   -XX:NodeCountInliningCutoff=220000
        *                   -XX:DesiredMethodLimit=100000
        *                   -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=0
      - *                   compiler.c2.TestScalarReplacementMaxLiveNodes
      + *                   compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
        */
      -package compiler.c2;
      +package compiler.escapeAnalysis;
       
       public class TestScalarReplacementMaxLiveNodes {
           public static void main(String[] args) {
      diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java b/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java
      new file mode 100644
      index 000000000000..fe310bb9f46e
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java
      @@ -0,0 +1,67 @@
      +/*
      + * Copyright (c) 2026 IBM Corporation. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +/*
      + * @test
      + * @bug 8390467
      + * @summary C2: _map != nullptr assert failure in LibraryCallKit::inline_Class_cast()
      + * @run main/othervm  -XX:CompileOnly=${test.main.class}::test1 -Xcomp ${test.main.class}
      + */
      +
      +package compiler.intrinsics;
      +
      +public class TestClassCastDeadPath {
      +    public static void main(String[] args) {
      +        B b = new B();
      +        C c = new C();
      +        A.class.cast(b);
      +        try {
      +            test1(c);
      +        } catch (ClassCastException cce) {
      +        }
      +    }
      +
      +    private static void test1(Object o) {
      +        if (!(o instanceof I)) {
      +            throw new RuntimeException("never taken");
      +        }
      +        A.class.cast(o);
      +    }
      +
      +    static abstract class A {
      +
      +    }
      +
      +    static class B extends A {
      +
      +    }
      +
      +    interface I {
      +
      +    }
      +
      +    static class C implements I {
      +
      +    }
      +
      +}
      diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java
      deleted file mode 100644
      index c48b24f7c567..000000000000
      --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java
      +++ /dev/null
      @@ -1,128 +0,0 @@
      -/*
      - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -package compiler.intrinsics.string;
      -
      -import compiler.lib.ir_framework.DontInline;
      -import compiler.lib.ir_framework.Run;
      -import compiler.lib.ir_framework.Test;
      -import compiler.lib.ir_framework.TestFramework;
      -
      -import java.lang.invoke.MethodHandle;
      -import java.lang.invoke.MethodHandles;
      -import java.lang.invoke.MethodType;
      -
      -import jdk.test.lib.Asserts;
      -
      -/*
      - * @test
      - * @bug 8373591
      - * @summary Verify that StringLatin1::inflate, StringUTF16::compress, and
      - *          StringCoding::implEncodeAsciiArray are scheduled properly
      - * @library /test/lib /
      - * @modules java.base/java.lang:+open
      - * @run driver ${test.main.class}
      - */
      -public class TestAntiDependency {
      -    static final MethodHandle COMPRESS_HANDLE;
      -    static final MethodHandle INFLATE_HANDLE;
      -    static final MethodHandle ENCODE_ISO_HANDLE;
      -    static {
      -        try {
      -            var currentLookup = MethodHandles.lookup();
      -            var stringLookup = MethodHandles.privateLookupIn(String.class, currentLookup);
      -            Class stringUtf16Class = stringLookup.findClass("java.lang.StringUTF16");
      -            var stringUtf16Lookup = MethodHandles.privateLookupIn(stringUtf16Class, currentLookup);
      -            COMPRESS_HANDLE = stringUtf16Lookup.findStatic(stringUtf16Class, "compress0",
      -                    MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class));
      -            Class stringLatin1Class = stringLookup.findClass("java.lang.StringLatin1");
      -            var stringLatin1Lookup = MethodHandles.privateLookupIn(stringLatin1Class, currentLookup);
      -            INFLATE_HANDLE = stringLatin1Lookup.findStatic(stringLatin1Class, "inflate0",
      -                    MethodType.methodType(void.class, byte[].class, int.class, char[].class, int.class, int.class));
      -            Class stringCodingClass = stringLookup.findClass("java.lang.StringCoding");
      -            ENCODE_ISO_HANDLE = stringLookup.findStatic(stringCodingClass, "encodeAsciiArray0",
      -                    MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class));
      -        } catch (Exception e) {
      -            throw new RuntimeException(e);
      -        }
      -    }
      -
      -    public static void main(String[] args) {
      -        var testFramework = new TestFramework();
      -        testFramework.setDefaultWarmup(1);
      -        testFramework.addFlags("--add-opens=java.base/java.lang=ALL-UNNAMED");
      -        testFramework.start();
      -    }
      -
      -    @DontInline
      -    static void consume(Object o1, Object o2) {}
      -
      -    @Test
      -    static int testStringCompress() throws Throwable {
      -        byte[] dst = new byte[4];
      -        char[] src = new char[4];
      -        consume(dst, src);
      -
      -        // The compiler must not schedule this after the store to src, either by having
      -        // StringCompressedCopyNode kill the whole memory, or by taking into consideration the
      -        // anti-dependency between 2 nodes
      -        int _ = (int) COMPRESS_HANDLE.invokeExact(src, 0, dst, 0, 4);
      -        src[0] = 1;
      -        return dst[0];
      -    }
      -
      -    @Test
      -    static int testStringInflate() throws Throwable {
      -        char[] dst = new char[4];
      -        byte[] src = new byte[4];
      -        consume(dst, src);
      -
      -        // The compiler must not schedule this after the store to src, either by having
      -        // StringInflatedCopyNode kill the whole memory, or by taking into consideration the
      -        // anti-dependency between 2 nodes
      -        INFLATE_HANDLE.invokeExact(src, 0, dst, 0, 4);
      -        src[0] = 1;
      -        return dst[0];
      -    }
      -
      -    @Test
      -    static int testEncodeISO() throws Throwable {
      -        byte[] dst = new byte[4];
      -        char[] src = new char[4];
      -        consume(dst, src);
      -
      -        // The compiler must not schedule this after the store to src, either by having
      -        // EncodeISOArrayNode kill the whole memory, or by taking into consideration the
      -        // anti-dependency between 2 nodes
      -        int _ = (int) ENCODE_ISO_HANDLE.invokeExact(src, 0, dst, 0, 4);
      -        src[0] = 1;
      -        return dst[0];
      -    }
      -
      -    @Run(test = {"testStringCompress", "testStringInflate", "testEncodeISO"})
      -    public void run() throws Throwable {
      -        Asserts.assertEQ(0, testStringCompress());
      -        Asserts.assertEQ(0, testStringInflate());
      -        Asserts.assertEQ(0, testEncodeISO());
      -    }
      -}
      diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java
      new file mode 100644
      index 000000000000..cb2b37b75bd0
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java
      @@ -0,0 +1,156 @@
      +/*
      + * 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 8390546
      + * @summary Verify that the memory effect of the encodeISOArray intrinsic is correctly wired in.
      + * @library /test/lib
      + * @requires vm.compiler2.enabled
      + * @modules java.base/java.lang:+open java.base/sun.nio.cs:+open
      + * @run main compiler.intrinsics.string.TestEncodeISOArray
      + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=100
      + *                   -XX:+IgnoreUnrecognizedVMOptions -XX:UseAVX=0 -XX:-UseSSE42Intrinsics
      + *                   compiler.intrinsics.string.TestEncodeISOArray
      + */
      +
      +package compiler.intrinsics.string;
      +
      +import jdk.test.lib.Asserts;
      +
      +import java.lang.invoke.MethodHandle;
      +import java.lang.invoke.MethodHandles;
      +import java.lang.invoke.MethodType;
      +import java.nio.ByteBuffer;
      +import java.nio.ByteOrder;
      +import java.nio.CharBuffer;
      +import java.nio.charset.StandardCharsets;
      +import java.util.Arrays;
      +
      +public class TestEncodeISOArray {
      +    private static final int ITERATIONS = 20_000;
      +    private static final int FIRST_BYTE = 'C';
      +    private static final char[] SOURCE = "C2 go brrr".toCharArray();
      +    private static final byte[] UTF16_SOURCE = toUTF16Bytes(SOURCE);
      +    private static final byte[] EXPECTED = "C2 go brrr".getBytes(StandardCharsets.UTF_8);
      +    private static final MethodType ENCODE_TYPE = MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class);
      +    private static final MethodType ENCODE_BYTE_TYPE = MethodType.methodType(int.class, byte[].class, int.class, byte[].class, int.class, int.class);
      +    private static final MethodHandle ENCODE_ASCII_ARRAY = findEncoder("java.lang.StringCoding", "encodeAsciiArray0", ENCODE_TYPE);
      +    private static final MethodHandle ENCODE_ISO_ARRAY = findEncoder("sun.nio.cs.ISO_8859_1$Encoder", "encodeISOArray0", ENCODE_TYPE);
      +    private static final MethodHandle ENCODE_BYTE_ISO_ARRAY = findEncoder("java.lang.StringCoding", "encodeISOArray0", ENCODE_BYTE_TYPE);
      +
      +    private static byte[] toUTF16Bytes(char[] chars) {
      +        ByteBuffer buffer = ByteBuffer.allocate(chars.length * Character.BYTES).order(ByteOrder.nativeOrder());
      +        for (char c : chars) {
      +            buffer.putChar(c);
      +        }
      +        return buffer.array();
      +    }
      +
      +    private static MethodHandle findEncoder(String className, String methodName, MethodType type) {
      +        try {
      +            Class holder = Class.forName(className);
      +            MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(holder, MethodHandles.lookup());
      +            return lookup.findStatic(holder, methodName, type);
      +        } catch (ReflectiveOperationException e) {
      +            throw new ExceptionInInitializerError(e);
      +        }
      +    }
      +
      +    // Original reproducer from JDK-8390546
      +    private static byte[] toUtf8Bytes(char[] chars) {
      +        ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chars));
      +        return Arrays.copyOfRange(byteBuffer.array(), 0, byteBuffer.limit());
      +    }
      +
      +    // Targeted check that does not depend on the UTF-8 encoder and arraycopy both being inlined.
      +    private static int encodeASCIIAndLoadFirstByte() throws Throwable {
      +        byte[] destination = new byte[4];
      +        int encoded = (int) ENCODE_ASCII_ARRAY.invokeExact(SOURCE, 0, destination, 0, 4);
      +        if (encoded != 4) {
      +            return -1;
      +        }
      +        return destination[0];
      +    }
      +
      +    private static int encodeISOAndLoadFirstByte() throws Throwable {
      +        byte[] destination = new byte[4];
      +        int encoded = (int) ENCODE_ISO_ARRAY.invokeExact(SOURCE, 0, destination, 0, 4);
      +        if (encoded != 4) {
      +            return -1;
      +        }
      +        return destination[0];
      +    }
      +
      +    private static int encodeByteISOAndLoadFirstByte() throws Throwable {
      +        byte[] destination = new byte[4];
      +        int encoded = (int) ENCODE_BYTE_ISO_ARRAY.invokeExact(UTF16_SOURCE, 0, destination, 0, 4);
      +        if (encoded != 4) {
      +            return -1;
      +        }
      +        return destination[0];
      +    }
      +
      +    private static boolean runOriginalReproducer() {
      +        for (int i = 0; i < ITERATIONS; i++) {
      +            if (!Arrays.equals(toUtf8Bytes(SOURCE), EXPECTED)) {
      +                return false;
      +            }
      +        }
      +        return true;
      +    }
      +
      +    private static boolean runASCIITest() throws Throwable {
      +        for (int i = 0; i < ITERATIONS; i++) {
      +            if (encodeASCIIAndLoadFirstByte() != FIRST_BYTE) {
      +                return false;
      +            }
      +        }
      +        return true;
      +    }
      +
      +    private static boolean runISOTest() throws Throwable {
      +        for (int i = 0; i < ITERATIONS; i++) {
      +            if (encodeISOAndLoadFirstByte() != FIRST_BYTE) {
      +                return false;
      +            }
      +        }
      +        return true;
      +    }
      +
      +    private static boolean runByteISOTest() throws Throwable {
      +        for (int i = 0; i < ITERATIONS; i++) {
      +            if (encodeByteISOAndLoadFirstByte() != FIRST_BYTE) {
      +                return false;
      +            }
      +        }
      +        return true;
      +    }
      +
      +    public static void main(String[] args) throws Throwable {
      +        Asserts.assertTrue(runOriginalReproducer(), "Original reproducer failed");
      +        Asserts.assertTrue(runASCIITest(), "ASCII encoding failed");
      +        Asserts.assertTrue(runISOTest(), "ISO-8859-1 encoding failed");
      +        Asserts.assertTrue(runByteISOTest(), "Byte ISO-8859-1 encoding failed");
      +    }
      +}
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      index b2961b8ba2bf..6fad572fd37f 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      @@ -28,7 +28,6 @@
       import compiler.lib.ir_framework.shared.CheckedTestFrameworkException;
       import compiler.lib.ir_framework.shared.TestFormat;
       import compiler.lib.ir_framework.shared.TestFormatException;
      -import compiler.valhalla.inlinetypes.InlineTypeIRNode;
       import jdk.test.lib.Platform;
       import jdk.test.whitebox.WhiteBox;
       
      @@ -152,12 +151,6 @@ public class IRNode {
            * }
            */
       
      -    // Valhalla: Make sure that all Valhalla specific IR nodes are also properly initialized. Doing it here also
      -    //           ensures that the Flag VM is able to pick up the correct compile phases.
      -    static {
      -        InlineTypeIRNode.forceStaticInitialization();
      -    }
      -
           public static final String ABS_D = PREFIX + "ABS_D" + POSTFIX;
           static {
               beforeMatchingNameRegex(ABS_D, "AbsD");
      @@ -3362,6 +3355,74 @@ public static void anyStoreOfNodes(String irNodePlaceholder, String fieldHolder)
               beforeMatchingNameRegex(OPAQUE_CONSTANT_BOOL, "OpaqueConstantBool");
           }
       
      +    /*
      +     * Inline type nodes.
      +     */
      +
      +    public static final String CALL_UNSAFE = PREFIX + "CALL_UNSAFE" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(CALL_UNSAFE, "# Static  jdk.internal.misc.Unsafe::");
      +    }
      +
      +    public static final String STORE_INLINE_FIELDS = PREFIX + "STORE_INLINE_FIELDS" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(STORE_INLINE_FIELDS, "store_inline_type_fields");
      +    }
      +
      +    public static final String LOAD_UNKNOWN_INLINE = PREFIX + "LOAD_UNKNOWN_INLINE" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(LOAD_UNKNOWN_INLINE, "load_unknown_inline_blob \\(C2 runtime\\)");
      +    }
      +
      +    public static final String STORE_UNKNOWN_INLINE = PREFIX + "STORE_UNKNOWN_INLINE" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(STORE_UNKNOWN_INLINE, "store_unknown_inline_blob \\(C2 runtime\\)");
      +    }
      +
      +    public static final String INLINE_ARRAY_NULL_GUARD = PREFIX + "INLINE_ARRAY_NULL_GUARD" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(INLINE_ARRAY_NULL_GUARD, "null_check' action='none'");
      +    }
      +
      +    public static final String CLONE_INTRINSIC_SLOW_PATH = PREFIX + "CLONE_INTRINSIC_SLOW_PATH" + POSTFIX;
      +    static {
      +        staticCallOfMethodNodes(CLONE_INTRINSIC_SLOW_PATH, "java.lang.Object::clone");
      +    }
      +
      +    public static final String JLONG_ARRAYCOPY = PREFIX + "JLONG_ARRAYCOPY" + POSTFIX;
      +    static {
      +        callLeafNoFpOfMethodNodes(JLONG_ARRAYCOPY, "jlong_disjoint_arraycopy");
      +    }
      +
      +    // The following nodes are specific to tests in in compiler/valhalla/inlinetypes using one of the MyValue classes.
      +    private static final String MYVALUE_KLASS = "compiler/valhalla/inlinetypes/.*MyValue\\w*";
      +    public static final String ALLOC_OF_MYVALUE_KLASS = PREFIX + "ALLOC_OF_MYVALUE_KLASS" + POSTFIX;
      +    static {
      +        allocateOfNodes(ALLOC_OF_MYVALUE_KLASS, MYVALUE_KLASS);
      +    }
      +
      +    public static final String ALLOC_ARRAY_OF_MYVALUE_KLASS = PREFIX + "ALLOC_ARRAY_OF_MYVALUE_KLASS" + POSTFIX;
      +    static {
      +        allocateArrayOfNodes(ALLOC_ARRAY_OF_MYVALUE_KLASS, MYVALUE_KLASS);
      +    }
      +
      +    private static final String ANY_KLASS = "compiler/valhalla/inlinetypes/[\\w/]*";
      +
      +    // TODO: Revisit with JDK-8380875
      +    public static final String LOAD_OF_ANY_KLASS = PREFIX + "LOAD_OF_ANY_KLASS" + POSTFIX;
      +    static {
      +        String loadNode = "Load(B|UB|S|US|I|L|F|D|P|N)";
      +        String valueClass = "@instptr:" + ANY_KLASS;
      +        String regex = START + loadNode + MID + valueClass + END;
      +        beforeMatching(LOAD_OF_ANY_KLASS, regex);
      +    }
      +
      +    // TODO: Revisit with JDK-8380875
      +    public static final String STORE_OF_ANY_KLASS = PREFIX + "STORE_OF_ANY_KLASS" + POSTFIX;
      +    static {
      +        anyStoreOfNodes(STORE_OF_ANY_KLASS, ANY_KLASS);
      +    }
      +
           /*
            * Utility methods to set up IR_NODE_MAPPINGS.
            */
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
      index bfffbdbec7ea..baed93594597 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
      @@ -36,6 +36,8 @@
       import jdk.test.lib.process.ProcessTools;
       
       import java.io.File;
      +import java.io.PrintWriter;
      +import java.io.StringWriter;
       import java.util.*;
       import java.util.regex.Matcher;
       import java.util.regex.Pattern;
      @@ -49,12 +51,15 @@
        * @see TestFrameworkSocket
        */
       public class TestVMProcess {
      -    private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
           private static final boolean PREFER_COMMAND_LINE_FLAGS = Boolean.getBoolean("PreferCommandLineFlags");
           private static final int WARMUP_ITERATIONS = Integer.getInteger("Warmup", -1);
           private static final boolean VERIFY_VM = Boolean.getBoolean("VerifyVM") && Platform.isDebugBuild();
      -    private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
      +    private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
           private static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom");
      +    private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
      +    private static final boolean DUMP_OUTPUT = VERBOSE || EXCLUDE_RANDOM || REPORT_STDOUT;
      +
      +    private static final String FATAL_ERROR_MARKER = "# A fatal error has been detected by the Java Runtime Environment:";
       
           private static String lastTestVMOutput = "";
       
      @@ -72,11 +77,131 @@ public TestVMProcess(List additionalFlags, Class testClass, Set>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
      +                                       "Make sure to define a set of at least one @Test method");
      +    }
      +
      +    private TestVMException createTestVMExceptionForNonZeroExit(TestFrameworkSocket socket, boolean allowNotCompilable) {
      +        String secondaryException = "";
      +        try {
      +            readAndDumpTestVmData(socket, allowNotCompilable);
      +        } catch (RuntimeException e) {
      +            // We observed a message processing exception. We treat it as secondary failure because messages could be
      +            // incomplete when the VM crashed or not even sent by the Test VM when it exits early on start-up
      +            // (e.g. passing in an unknown VM flag).
      +            secondaryException = buildSecondaryExceptionInfo(e);
      +        }
      +        // Primary exception: non-zero Test VM exit.
      +        return new TestVMException(buildExceptionInfo() + secondaryException);
      +    }
      +
      +    private String buildSecondaryExceptionInfo(RuntimeException e) {
      +        String secondaryException;
      +        StringWriter stringWriter = new StringWriter();
      +        e.printStackTrace(new PrintWriter(stringWriter));
      +
      +        secondaryException = System.lineSeparator() +
      +                             "Secondary Message-Processing Exception" + System.lineSeparator() +
      +                             "--------------------------------------" + System.lineSeparator() +
      +                             stringWriter;
      +        return secondaryException;
      +    }
      +
      +    /**
      +     * Get more detailed information about the exception in a pretty format.
      +     */
      +    private String buildExceptionInfo() {
      +        StringBuilder builder = new StringBuilder();
      +        builder.append("Test VM exited with code ").append(oa.getExitValue()).append(System.lineSeparator());
      +        if (hasFatalErrorMarker() || DUMP_OUTPUT) {
      +            // Also dump the Test VM output if we experience a JVM error to show assertion failures etc.
      +            builder.append(System.lineSeparator())
      +                   .append(System.lineSeparator())
      +                   .append("Test VM - Standard Output").append(System.lineSeparator())
      +                   .append("-------------------------").append(System.lineSeparator())
      +                   .append(oa.getStdout());
      +        }
      +        builder.append(System.lineSeparator())
      +               .append(commandLine)
      +               .append(System.lineSeparator())
      +               .append(System.lineSeparator())
      +               .append("Test VM - Error Output").append(System.lineSeparator())
      +               .append("----------------------").append(System.lineSeparator())
      +               .append(oa.getStderr())
      +               .append(System.lineSeparator())
      +               .append(System.lineSeparator());
      +        return builder.toString();
      +    }
      +
      +    /**
      +     * Best-effort VM crash detection by matching the start of the fatal error message. This covers most of the crashes
      +     * but fails when the Test VM was killed externally or when the output is unavailable or truncated which could
      +     * happen in native stack overflow cases.
      +     */
      +    private boolean hasFatalErrorMarker() {
      +        return oa.getExitValue() != 0 && oa.getOutput().contains(FATAL_ERROR_MARKER);
           }
       
           public String getCommandLine() {
      @@ -173,55 +298,4 @@ private void start() {
                             + System.lineSeparator();
               lastTestVMOutput = oa.getOutput();
           }
      -
      -    private void checkTestVMExitCode() {
      -        final int exitCode = oa.getExitValue();
      -        if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) {
      -            System.out.println("--- OUTPUT TestFramework Test VM ---");
      -            System.out.println(oa.getOutput());
      -        }
      -
      -        if (exitCode != 0) {
      -            throwTestVMException();
      -        }
      -    }
      -
      -    /**
      -     * Exit code was non-zero of Test VM. Check the stderr to determine what kind of exception that should be thrown to
      -     * react accordingly later.
      -     */
      -    private void throwTestVMException() {
      -        String stdErr = oa.getStderr();
      -        if (stdErr.contains("TestFormat.throwIfAnyFailures")) {
      -            Pattern pattern = Pattern.compile("Violations \\(\\d+\\)[\\s\\S]*(?=/============/)");
      -            Matcher matcher = pattern.matcher(stdErr);
      -            TestFramework.check(matcher.find(), "Must find violation matches");
      -            throw new TestFormatException(System.lineSeparator() + System.lineSeparator() + matcher.group());
      -        } else if (stdErr.contains("NoTestsRunException")) {
      -            throw new NoTestsRunException(">>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
      -                                          "Make sure to define a set of at least one @Test method");
      -        } else {
      -            throw new TestVMException(getExceptionInfo());
      -        }
      -    }
      -
      -    /**
      -     * Get more detailed information about the exception in a pretty format.
      -     */
      -    private String getExceptionInfo() {
      -        int exitCode = oa.getExitValue();
      -        String stdErr = oa.getStderr();
      -        String stdOut = "";
      -        boolean osIsWindows = Platform.isWindows();
      -        boolean JVMHadError = (!osIsWindows && exitCode == 134) || (osIsWindows && exitCode == -1);
      -        if (JVMHadError) {
      -            // Also dump the stdout if we experience a JVM error (e.g. to show hit assertions etc.).
      -            stdOut = System.lineSeparator() + System.lineSeparator() + "Standard Output" + System.lineSeparator()
      -                     + "---------------" + System.lineSeparator() + oa.getOutput();
      -        }
      -        return "TestFramework Test VM exited with code " + exitCode + System.lineSeparator() + stdOut
      -               + System.lineSeparator() + commandLine + System.lineSeparator() + System.lineSeparator()
      -               + "Error Output" + System.lineSeparator() + "------------" + System.lineSeparator() + stdErr
      -               + System.lineSeparator() + System.lineSeparator();
      -    }
       }
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
      index 05359d0d789e..bb7765c5bc49 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
      @@ -30,6 +30,8 @@
       import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages;
       import compiler.lib.ir_framework.test.network.TestVmSocket;
       
      +import jdk.test.lib.Utils;
      +
       import java.io.BufferedReader;
       import java.io.IOException;
       import java.io.InputStreamReader;
      @@ -41,16 +43,36 @@
        */
       public class TestFrameworkSocket implements AutoCloseable {
           private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port";
      +    private static final int SOCKET_TIMEOUT_IN_MS = (int)Utils.adjustTimeout(10_000L);
       
           private final int serverSocketPort;
           private final ServerSocket serverSocket;
           private final ExecutorService acceptExecutor;
           private final ExecutorService clientExecutor;
       
      -    // Make these volatile such that the main thread can observe an update written by the worker threads in the executor
      -    // services to avoid stale values.
      +    /*
      +     * CompletableFuture shared by the Driver VM and the accept/reader threads.
      +     *
      +     * Lifecycle:
      +     * 1. The future is created before the accept loop task is submitted.
      +     * 2. The Driver VM starts the Test VM. The socket and the executors remain open while the Driver VM waits on the
      +     *    future to be completed.
      +     * 3. The accept thread accepts the Test VM connection and reads the identity handshake.
      +     * 4. The accept thread now schedules a reader for incoming Test VM messages.
      +     * 5. During normal execution, the Test VM closes the connection before exiting. The reader completes the future
      +     *    with the parsed Test VM messages.
      +     * 6. Accepting, identity handshake, task submission, or message reading failures complete the future exceptionally.
      +     * 7. The Driver VM obtains the result, observes a failure, or times out before the socket and executors are closed.
      +     *
      +     * Note: The future must be created eagerly such that the Driver VM can wait on it even before the accept thread
      +     *       has accepted the Test VM connection. The accept thread might only be scheduled after the Test VM has exited.
      +     *       This is possible because the server socket is already listening and the OS can queue the connection until
      +     *       the accept thread processes it.
      +     */
      +    private final CompletableFuture javaMessagesFuture;
      +
      +    // Written by the Driver VM thread and read by the accept thread.
           private volatile boolean running;
      -    private volatile Future javaFuture;
       
           public TestFrameworkSocket() {
               try {
      @@ -62,6 +84,7 @@ public TestFrameworkSocket() {
               serverSocketPort = serverSocket.getLocalPort();
               acceptExecutor = Executors.newSingleThreadExecutor();
               clientExecutor = Executors.newCachedThreadPool();
      +        javaMessagesFuture = new CompletableFuture<>();
               if (TestFramework.VERBOSE) {
                   System.out.println("TestFramework server socket uses port " + serverSocketPort);
               }
      @@ -73,50 +96,40 @@ public String getPortPropertyFlag() {
       
           public void start() {
               running = true;
      -        CountDownLatch calledAcceptLoopLatch = new CountDownLatch(1);
      -        startAcceptLoop(calledAcceptLoopLatch);
      -    }
      -
      -    private void startAcceptLoop(CountDownLatch calledAcceptLoopLatch) {
      -        acceptExecutor.submit(() -> acceptLoop(calledAcceptLoopLatch));
      -        waitUntilAcceptLoopRuns(calledAcceptLoopLatch);
      -    }
      -
      -    private void waitUntilAcceptLoopRuns(CountDownLatch calledAcceptLoopLatch) {
      -        try {
      -            if (!calledAcceptLoopLatch.await(10, TimeUnit.SECONDS)) {
      -                throw new IllegalStateException("acceptLoop did not start in time");
      -            }
      -        } catch (Exception e) {
      -            throw new TestFrameworkException("Could not start TestFrameworkSocket", e);
      -        }
      +        acceptExecutor.submit(this::acceptLoop);
           }
       
           /**
            * Main loop to wait for new client connections and handling them upon connection request.
            */
      -    private void acceptLoop(CountDownLatch calledAcceptLoopLatch) {
      -        calledAcceptLoopLatch.countDown();
      +    private void acceptLoop() {
               while (running) {
                   try {
                       acceptNewClientConnection();
      -            }  catch (SocketException e) {
      +            } catch (SocketException e) {
                       if (!running || serverSocket.isClosed()) {
                           // Normal shutdown
                           return;
                       }
      -                running = false;
      -                throw new TestFrameworkException("Server socket error", e);
      +                throwServerSocketError(e);
                   } catch (TestFrameworkException e) {
      -                running = false;
      -                throw e;
      +                throwTestFrameworkException(e);
                   } catch (Exception e) {
      -                running = false;
      -                throw new TestFrameworkException("Server socket error", e);
      +                throwServerSocketError(e);
                   }
               }
           }
       
      +    private void throwServerSocketError(Exception e) {
      +        throwTestFrameworkException(new TestFrameworkException("Server socket error", e));
      +    }
      +
      +    private void throwTestFrameworkException(TestFrameworkException testFrameworkException) {
      +        running = false;
      +        javaMessagesFuture.completeExceptionally(testFrameworkException);
      +        throw testFrameworkException;
      +    }
      +
           /**
            * Accept new client connection by first reading the identity of the connection (either coming from Java or C2)
            * and then submitting a task accordingly to manage incoming messages on that connection/socket.
      @@ -137,11 +150,11 @@ private void acceptNewClientConnection() throws IOException {
           private String readIdentity(Socket client, BufferedReader reader) throws IOException {
               String identity;
               try {
      -            client.setSoTimeout(10000);
      +            client.setSoTimeout(SOCKET_TIMEOUT_IN_MS);
                   identity = reader.readLine();
                   TestFramework.check(identity != null, "end of stream has been reached without reading the identity");
               } catch (SocketTimeoutException e) {
      -            throw new TestFrameworkException("Did not receive initial identity message after 10s", e);
      +            throw new TestFrameworkException("Timed out while waiting for initial identity message", e);
               } finally {
                   client.setSoTimeout(0);
               }
      @@ -154,7 +167,9 @@ private String readIdentity(Socket client, BufferedReader reader) throws IOExcep
            */
           private void submitTask(String identity, Socket client, BufferedReader reader) {
               if (identity.equals(TestVmSocket.IDENTITY)) {
      -            javaFuture = clientExecutor.submit(new TestVmMessageReader<>(client, reader, new JavaMessageParser()));
      +            TestVmMessageReader messageReader =
      +                    new TestVmMessageReader<>(client, reader, new JavaMessageParser());
      +            javaMessagesFuture.completeAsync(messageReader::call, clientExecutor);
               } else {
                   throw new TestFrameworkException("Unrecognized identity: " + identity);
               }
      @@ -179,9 +194,26 @@ public TestVMData testVmData(String hotspotPidFileName, boolean allowNotCompilab
       
           private JavaMessages testVmMessages() {
               try {
      -            return javaFuture.get();
      +            // Note: The Test VM may have already exited while the accept and message reader thread are still processing
      +            //       the connection. Let's wait until they are finished.
      +            return javaMessagesFuture.get(SOCKET_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
               } catch (ExecutionException e) {
                   throw new TestFrameworkException("No test VM messages were received", e);
      +        } catch (TimeoutException e) {
      +            throw new RuntimeException("Timed out while waiting for Test VM messages." + System.lineSeparator() +
      +                                        System.lineSeparator() +
      +                                        "Did any of the following happen?" + System.lineSeparator() +
      +                                        "(1) TestFramework.addFlags(-DReproduce=true)" + System.lineSeparator() +
      +                                        "(2) TestFramework.addFlags(--version) or any other VM flag that prevents " +
      +                                        " TestVM.main() from being called?" + System.lineSeparator() +
      +                                        "(3) The Test VM crashed before calling TestVM.main()" + System.lineSeparator() +
      +                                        System.lineSeparator() +
      +                                        "(1) and (2) are unsupported and are expected to fail." + System.lineSeparator() +
      +                                        "-> Please change your test!" + System.lineSeparator() +
      +                                        "(3) The IR Framework cannot handle early VM crashes." + System.lineSeparator() +
      +                                        "-> Please change your test if such a crash was anticipated!" +
      +                                        System.lineSeparator() + System.lineSeparator() +
      +                                        "In all other cases, please file an IR Framework bug!", e);
               } catch (Exception e) {
                   throw new TestFrameworkException("Error while fetching Test VM Future", e);
               }
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      index 9c19ed4c8ef9..9f1687e8cfdf 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      @@ -446,11 +446,15 @@ private boolean check(String flag, String value) {
                   TestFormat.failNoThrow("Provided empty flag" + failAt());
                   return false;
               }
      +        Object actualFlagValue = WHITE_BOX.getStringVMFlag(flag);
      +        if (actualFlagValue != null) {
      +            return value.equals(actualFlagValue);
      +        }
               if (value.isEmpty()) {
                   TestFormat.failNoThrow("Provided empty value for flag " + flag + failAt());
                   return false;
               }
      -        Object actualFlagValue = WHITE_BOX.getBooleanVMFlag(flag);
      +        actualFlagValue = WHITE_BOX.getBooleanVMFlag(flag);
               if (actualFlagValue != null) {
                   return checkBooleanFlag(flag, value, (Boolean) actualFlagValue);
               }
      @@ -462,10 +466,6 @@ private boolean check(String flag, String value) {
               if (actualFlagValue != null) {
                   return checkFlag(Double::parseDouble, "floating point", flag, value, (Double) actualFlagValue);
               }
      -        actualFlagValue = WHITE_BOX.getStringVMFlag(flag);
      -        if (actualFlagValue != null) {
      -            return value.equals(actualFlagValue);
      -        }
               if (flag.equals("enable-valhalla")) {
                   return checkBooleanFlag(flag, value, Integer.class.isValue());
               }
      diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
      index 81a4bdd935b0..6dda7658523d 100644
      --- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
      +++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
      @@ -23,11 +23,15 @@
       
       package compiler.lib.template_framework;
       
      +import java.util.ArrayList;
      +import java.util.Collections;
       import java.util.function.BiFunction;
       import java.util.function.Function;
      +import java.util.function.IntFunction;
       import java.util.function.Supplier;
       
       import java.util.List;
      +import java.util.stream.IntStream;
       
       import compiler.lib.compile_framework.CompileFramework;
       import compiler.lib.ir_framework.TestFramework;
      @@ -1063,4 +1067,229 @@ static Token addStructuralName(String name, StructuralName.Type type) {
           static StructuralName.FilteredSet structuralNames() {
               return new StructuralName.FilteredSet();
           }
      +
      +
      +    /**
      +     * Create {@code count} times a {@link ScopeToken}. This can be used inside another {@link #scope}.
      +     *
      +     * 

      + * {@snippet lang=java: + * // Output: + * // scope + * // scope + * // scope + * Template.make(() -> scope( + * repeat(3, scope("scope\n")) + * )); + * } + * + * @param count How many times do we repeat the provided scope? + * @param scope The scope() method. + * @return A list of ScopeTokens. + */ + static List repeat(int count, ScopeToken scope) { + return repeat(count, _ -> scope); + } + + /** + * Same as {@link #repeat(int, ScopeToken)} but instead of a {@link ScopeToken}, this method takes a + * {@code scopeFactory} {@link IntFunction} that takes an integer index, denoting the current iteration index + * ranging from iteration 0 to count - 1, and passes it into a {@link #scope}. This can be used inside another + * {@link #scope}: + * + *

      + * {@snippet lang=java: + * // Output: + * // 1: scope for index 0 + * // 1: scope for index 1 + * // 2: scope for index 0 + * // 2: scope for index 1 + * // 2: scope for index 2 + * Template.make(() -> scope( + * repeat(2, index -> scope("1: scope for index " + index +"\n")), + * repeat(3, index -> scope("2: scope for index " + index +"\n")) + * )); + * } + * + * @param count How many times do we repeat the provided scope? + * @param scope The scope() function taking an iteration index. + * @return A list of ScopeTokens. + */ + static List repeat(int count, IntFunction scope) { + if (count < 0) { + throw new IllegalArgumentException("count must not be negative: " + count); + } + return IntStream.range(0, count) + .mapToObj(scope) + .toList(); + } + + /** + * Same as {@link #repeat(int, ScopeToken)} but join all resulting {@link ScopeToken}s together by using an + * additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between. + * + *

      + * {@snippet lang=java: + * // Output: + * // Element 1, Element 2, Element 3 + * Template.make(() -> scope( + * repeatAndJoin(3, ", ", (index) -> scope("Element " + index)) + * )); + * } + * + * @param count How many times do we repeat the provided scope? + * @param delimiter The delimiter to join the individual repeated scopes together. + * @param scope The scope() function. + * @return A list of ScopeTokens. + */ + static List repeatAndJoin(int count, String delimiter, ScopeToken scope) { + return repeatAndJoin(count, delimiter, (_) -> scope); + } + + /** + * Same as the indexed {@link #repeat(int, IntFunction)} but join all resulting {@link ScopeToken}s together by using + * an additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between. This is the indexed + * version of {@link #repeatAndJoin(int, String, ScopeToken)}. + * + *

      + * {@snippet lang=java: + * // Output: + * // Element 0, Element 1, Element 2 + * Template.make(() -> scope( + * repeatAndJoin(3, ", ", (index) -> scope("Element " + index)) + * )); + * } + * + * @param count How many times do we repeat the provided scope? + * @param delimiter The delimiter to join the individual repeated scopes together. + * @param scope The scope() function taking an iteration index. + * @return A list of ScopeTokens. + */ + static List repeatAndJoin(int count, String delimiter, IntFunction scope) { + if (count < 0) { + throw new IllegalArgumentException("count must not be negative: " + count); + } + + List scopeTokens = new ArrayList<>(); + for (int i = 0; i < count; i++) { + if (i > 0) { + // Add delimiter before the actual scope, skipping the very first element. + scopeTokens.add(scope(delimiter)); + } + // Create the actual scope. + scopeTokens.add(scope.apply(i)); + } + // Return an unmodifiable list. + return List.copyOf(scopeTokens); + } + + /** + * Map each element in {@code list} to the {@code scopeFactory} which is a {@link Function} taking an individual + * element of the {@code list} which is passed into a {@link #scope}. + * + *

      + * {@snippet lang=java: + * // Output: + * // Element 1 + * // Element 2 + * // Element 3 + * Template.make(() -> scope( + * map(List.of(1, 2, 3), element -> scope("Element " + element +"\n")) + * )); + * } + * + * @param list List of elements to be mapped to the provided scope() function. + * @param scope The scope() function taking a list element. + * @return A list of ScopeTokens. + */ + static List map(List list, Function scope) { + return map(list, (element, _) -> scope.apply(element)); + } + + /** + * Same as {@link #map(List, Function)} but the {@code scopeFactory} is a {@link BiFunction} that takes an additional + * integer index, denoting the current list index ranging from iteration 0 to count - 1. Each element and its list + * index element are passed into a {@link #scope}. + * + *

      + * {@snippet lang=java : + * // Output: + * // Element 1 at index 0 + * // Element 2 at index 1 + * // Element 3 at index 2 + * Template.make(() -> scope( + * map(List.of(1, 2, 3), (element, index) -> scope("Element " + element + " at index " + index + "\n")) + * )); + * } + * + * @param list List of elements to be mapped to the provided scope() function. + * @param scope The scope() function taking a list element and list index. + * @return A list of ScopeTokens. + */ + static List map(List list, BiFunction scope) { + List scopeTokens = new ArrayList<>(list.size()); + int i = 0; + for (T element : list) { + scopeTokens.add(scope.apply(element, i)); + i++; + } + + // Return an unmodifiable list. + return List.copyOf(scopeTokens); + } + + /** + * Same as {@link #map(List, Function)} but join all resulting {@link ScopeToken}s together by using an additional + * {@code delimiter} which is put into a separate {@link ScopeToken}s in between. + * + *

      + * {@snippet lang=java: + * // Output: + * // Element 1, Element 2, Element 3 + * Template.make(() -> scope( + * mapAndJoin(List.of(1, 2, 3), ", ", (element) -> scope("Element " + element)) + * )); + *} + * @param list List of elements to be mapped to the provided scope() function and then be joined with delimiter. + * @param delimiter The delimiter to join the individual scopes for all list elements together. + * @param scope The scope() function taking a list element. + * @return A list of ScopeTokens. + */ + static List mapAndJoin(List list, String delimiter, Function scope) { + return mapAndJoin(list, delimiter, (element, _) -> scope.apply(element)); + } + + /** + * Same as the indexed {@link #map(List, BiFunction)} but join all resulting {@link ScopeToken}s together by using + * an additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between. This is the indexed + * version of {@link #mapAndJoin(List, String, Function)}. + * + *

      + * {@snippet lang=java: + * // Output: + * // Element 1 at index 0, Element 2 at index 1, Element 3 at index 2 + * Template.make(() -> scope( + * mapAndJoin(List.of(1, 2, 3), ", ", (element, index) -> scope("Element " + element + " at index " + index)) + * )); + *} + * @param list List of elements to be mapped to the provided scope() function and then be joined with delimiter. + * @param delimiter The delimiter to join the individual scopes for all list elements together. + * @param scope The scope() function taking a list element and list index. + * @return A list of ScopeTokens. + */ + static List mapAndJoin(List list, String delimiter, BiFunction scope) { + List scopeTokens = new ArrayList<>(); + int i = 0; + for (T item : list) { + if (i > 0) { + // Add delimiter before the actual scope, skipping the very first element. + scopeTokens.add(scope(delimiter)); + } + // Create the actual scope. + scopeTokens.add(scope.apply(item, i)); + i++; + } + // Return an unmodifiable list. + return List.copyOf(scopeTokens); + } } diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java new file mode 100644 index 000000000000..1c9bdf153e8f --- /dev/null +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8387014 + * @summary Test that UTF-16 string concat overflow does not produce a negative size backing array + * @requires vm.compiler2.enabled & os.maxMemory > 4G + * @library /test/lib / + * @run main/othervm -Xmx4g -XX:-OptoScheduling ${test.main.class} + * @run main/othervm -Xmx4g -Xint ${test.main.class} + * @run main/othervm -Xmx4g -XX:-TieredCompilation -Xcomp -XX:-OptoScheduling + * -XX:CompileOnly=${test.main.class}::f + * ${test.main.class} + */ + +// The test uses -XX:-OptoScheduling to avoid the assert "too many D-U pinch points" on aarch64 (JDK-8328078). + +package compiler.stringopts; + +import jdk.test.lib.Asserts; + +public class TestStackedConcatsManyUTF16Overflow { + + public static void main (String... args) { + new StringBuilder(); // Trigger loading of the StringBuilder class. + try { + String s = f(); + String z = "🙂"; + for (int i = 0; i < 29; i++) { + z = z + z; + } + Asserts.assertEQ(s, z); + } catch (OutOfMemoryError e) { + Asserts.assertTrue(e.getMessage().equals("Required array length 1073741824 + 1073741824 is too large")); + // Specifically, we should not get "`main' threw exception: java.lang.NegativeArraySizeException: -2147483648" + return; + } + throw new RuntimeException("Unreachable."); + } + + static String f() { + + String s = "🙂"; // length() 2 UTF-16 string + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + + return s; + } +} diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java index 15fc03036749..1e6fd434804d 100644 --- a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.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,13 +23,15 @@ /* * @test - * @bug 8356246 + * @bug 8356246 8362117 * @summary Test stacked string concatenations where the toString of the first StringBuilder * is used as a shared test by two diamond Ifs in the second StringBuilder. - * @run main/othervm compiler.stringopts.TestStackedConcatsSharedTest - * @run main/othervm -XX:-TieredCompilation -Xcomp - * -XX:CompileOnly=compiler.stringopts.TestStackedConcatsSharedTest::* - * compiler.stringopts.TestStackedConcatsSharedTest + * (f): make sure we don't crash outright + * (g): external null checks depending on the same test/removed call should not give a wrong result. + * (h): multiple phis attached to the same diamond region; only one is a proper null check phi. + * (i): non-null check phi reused after intermediate stacked concat: check for correct result + * @run main/othervm ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:CompileOnly=${test.main.class}::* ${test.main.class} */ package compiler.stringopts; @@ -42,6 +44,21 @@ public static void main(String... args) { if (!s.equals("")) { throw new RuntimeException("wrong result"); } + s = g(); + if (!s.equals("abcabcabc")) { + System.out.println(s); + throw new RuntimeException("wrong result"); + } + s = h(); + if (!s.equals("abcabcnotnull")) { + System.out.println(s); + throw new RuntimeException("wrong result"); + } + s = i(); + if (!s.equals("abcabcnotnull")) { + System.out.println(s); + throw new RuntimeException("wrong result"); + } } static String f() { @@ -52,4 +69,37 @@ static String f() { s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString(); return s; } + + static String g() { + String s = "abc"; + s = new StringBuilder(s).toString(); + s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString() + (s == null ? "def" : "abc"); + return s; + } + + static String h() { + String s1 = new String("abc"); + String s2 = new StringBuilder(s1).append(s1).toString(); + String arg2 = ""; + if (s2 == null) { + arg2 = "null"; + } else { + arg2 = "notnull"; + } + return new StringBuilder(s2).append(arg2).toString(); + } + + static String i() { + String s1 = new String("abc"); + String s2 = new StringBuilder(s1).append(s1).toString(); + String arg2 = ""; + if (s2 == null) { + arg2 = "null"; + } else { + arg2 = "notnull"; + } + String s3 = new StringBuilder(s2).toString(); + String s4 = new StringBuilder(s3).append(arg2).toString(); + return s4; + } } diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java new file mode 100644 index 000000000000..0ead4d2661cd --- /dev/null +++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8362117 + * @summary Basic IR checks to verify that merge validation does not break concat optimizations. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.stringopts; + +import compiler.lib.ir_framework.*; + +public class TestStringConcatIR { + + public static void main(String[] args) { + TestFramework.runWithFlags(); + } + + @Run(test = {"stackedConcat", "stackedConcatNullCheck"}) + public void runMethodA() { + stackedConcat(); + stackedConcatNullCheck(); + } + + @Test + @IR(counts = {IRNode.CALL, ">= 9"}, phase = {CompilePhase.BEFORE_STRINGOPTS}) // at least init, append, tostring x 3 + @IR(counts = {IRNode.ALLOC, "= 3"}, phase = {CompilePhase.BEFORE_STRINGOPTS}) + @IR(counts = {IRNode.CALL, "= 0"}, phase = {CompilePhase.ITER_GVN1}) + @IR(counts = {IRNode.ALLOC, "= 1"}, phase = {CompilePhase.ITER_GVN1}) + @IR(counts = {IRNode.STORE_B, "= 16"}, phase = {CompilePhase.ITER_GVN1}) + static String stackedConcat() { + String s = "ab"; + s = new StringBuilder(s).append(s).toString(); + s = new StringBuilder(s).append(s).toString(); + s = new StringBuilder(s).append(s).toString(); + return s; + } + + @Test + @IR(applyIf = {"TieredCompilation", "true"}, + counts = {IRNode.CALL, ">= 9", IRNode.ALLOC, "= 3"}, + phase = {CompilePhase.BEFORE_STRINGOPTS}) + @IR(applyIf = {"TieredCompilation", "true"}, + counts = {IRNode.CALL, "= 0", IRNode.ALLOC, "= 1", IRNode.STORE_B, "= 24"}, + phase = {CompilePhase.ITER_GVN1}) + static String stackedConcatNullCheck() { + String s = "abc"; + s = new StringBuilder(String.valueOf(s)).append(s).toString(); + s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString(); + s = new StringBuilder(s).append(String.valueOf(s)).toString(); + return s; + } +} diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java new file mode 100644 index 000000000000..b539c0e7cb5d --- /dev/null +++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8362117 + * @summary Prevent crashes and miscompilations when external constructs + * could be confused for StringConcat append/toString-null checks. + * @run main/othervm ${test.main.class} + * @run main/othervm -Xbatch + * -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + * @run main/othervm -Xbatch + * -XX:CompileThreshold=500 + * -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + * @run main/othervm -Xbatch + * -XX:-TieredCompilation + * -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + */ + +package compiler.stringopts; + +public class TestStringConcatValidateMerge { + + public static void main (String... args) { + + String gold = test1(false); + for (int i = 0; i < 10_000; i++) { + test1((i & 1) == 0); + } + String val = test1(false); + if (!val.equals(gold)) { + throw new RuntimeException("wrong value: " + val + " vs " + gold); + } + + for (int t = 0; t < 10_000; t++) { + // The following line is probably important for profiling. + try { new String((String) null); } catch (NullPointerException e) {} + test2(); + } + + for (int t = 0; t < 10_000; t++) { + try { + if (t % 2 != 0) { + test3(null, "B"); + } else { + test3("A", null); + } + } catch (NullPointerException e) { + // expected + } + } + + for (int i = 0; i < 100_000; i++) { + test4(); + } + + for (int i = 0; i < 100_000; i++) { + test5(i % 2 == 0); + } + + gold = test6(new StringBuilder(" ")); + for (int i = 0; i < 100_000; i++) { + val = test6(new StringBuilder(" ")); + } + if (!val.equals(gold)) { + throw new RuntimeException("wrong result."); + } + + } + + // test1-3: StringOpts can't stack as SB1 is used in a compare in SB2 (previously confused as a valid string null check). + // test4: can't remove SB1's toString as it's used in an external comparison that needs it -> reject stacking + // test5: hand-written branching that changes return value (previously mistaken to be a valid string null check). + // test6: merge an unresolved stringbuilder with the intermediate value used in a compare: reject single concat. + + // JDK-8385429 + static String test1(boolean flag) { + String s = new StringBuilder("ABC").toString(); + return new StringBuilder().append(s).append(s == null ? "x" : "y").append(flag ? "z" : s).toString(); + } + + // JDK-8385428 + static int test2() { + String s1 = (("a" == null) ? "b" : "c") + 'd'; + String s2 = new StringBuilder(s1).toString(); + String s3 = new StringBuilder(s2).append(s1).append(s2 == null ? "" : s2).toString(); + return s3.length(); + } + + // JDK-8385415 + static Object test3(String a, String b) { + String s1 = new String(b); + String s2 = new StringBuffer(s1).append(s1).toString(); + return new StringBuffer(s2).append(a).append(s2 == null ? "" : s2).toString(); + } + + // JDK-8384130 + static String test4() { + String s = new StringBuilder().toString(); + return new StringBuilder(s).toString() == s ? "a" : "b"; + } + + static String test5(boolean test) { + String s1 = new String("b"); + String s2 = new StringBuilder(s1).append(s1).toString(); + String arg1 = ""; + String arg2 = ""; + if (s2 == null) { + arg2 = "null"; + } else { + arg2 = "Some other string"; + } + return new StringBuffer(s2).append(arg2).toString(); + } + + static String test6(StringBuilder c) { + StringBuilder s = new StringBuilder().append(" "); + String ret = s.append(s == c ? "abc" : " ").toString(); + return ret; + } + +} diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java new file mode 100644 index 000000000000..ca75545092bb --- /dev/null +++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8362117 + * @summary Similar type of test scenarios as in TestStringConcatValidateMerge.java + * but for problems which manifested with -Xcomp + * (f): stringopts shouldn't confuse ternary expression with string null check + * and fold away diamond phi arbitrarily leading to wrong result when depending on + * toString of SB1. + * (g): variant of (f) with append instead of toString + * @library /test/lib / + * @run main/othervm ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xcomp + * -XX:CompileOnly=${test.main.class}::* ${test.main.class} + */ + +package compiler.stringopts; + +import jdk.test.lib.Asserts; + +public class TestStringConcatValidateMergeXcomp { + + public static void main (String... args) { + new StringBuilder(); // load the class + f(); + g(); + } + + static String f() { + String s = "a"; + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append((s == "xx") ? s : "aa").toString(); + Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx"); + return s; + } + + static String g() { + String s = "a"; + StringBuilder sb0 = new StringBuilder(); + s = new StringBuilder().append(s).append(s).toString(); + StringBuilder sb2 = new StringBuilder().append(s); + s = sb2.append((sb2 == sb0) ? "xx" : "aa").toString(); + Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx"). + return s; + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java deleted file mode 100644 index b55622fc7da1..000000000000 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.valhalla.inlinetypes; - -import compiler.lib.ir_framework.IRNode; - -import static compiler.lib.ir_framework.IRNode.*; - -public class InlineTypeIRNode { - private static final String POSTFIX = "#I_"; - - public static final String CALL_UNSAFE = PREFIX + "CALL_UNSAFE" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(CALL_UNSAFE, InlineTypeRegexes.JDK_INTERNAL_MISC_UNSAFE); - } - - public static final String STORE_INLINE_FIELDS = PREFIX + "STORE_INLINE_FIELDS" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(STORE_INLINE_FIELDS, InlineTypeRegexes.STORE_INLINE_TYPE_FIELDS); - } - - public static final String LOAD_UNKNOWN_INLINE = PREFIX + "LOAD_UNKNOWN_INLINE" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(LOAD_UNKNOWN_INLINE, InlineTypeRegexes.LOAD_UNKNOWN_INLINE); - } - - public static final String STORE_UNKNOWN_INLINE = PREFIX + "STORE_UNKNOWN_INLINE" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(STORE_UNKNOWN_INLINE, InlineTypeRegexes.STORE_UNKNOWN_INLINE); - } - - public static final String INLINE_ARRAY_NULL_GUARD = PREFIX + "INLINE_ARRAY_NULL_GUARD" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(INLINE_ARRAY_NULL_GUARD, InlineTypeRegexes.INLINE_ARRAY_NULL_GUARD); - } - - public static final String CLONE_INTRINSIC_SLOW_PATH = PREFIX + "CLONE_INTRINSIC_SLOW_PATH" + POSTFIX; - static { - IRNode.staticCallOfMethodNodes(CLONE_INTRINSIC_SLOW_PATH, InlineTypeRegexes.JAVA_LANG_OBJECT_CLONE); - } - - public static final String CHECKCAST_ARRAYCOPY = PREFIX + "CHECKCAST_ARRAYCOPY" + POSTFIX; - static { - IRNode.callLeafNoFpOfMethodNodes(CHECKCAST_ARRAYCOPY, InlineTypeRegexes.CHECKCAST_ARRAYCOPY); - } - - public static final String JLONG_ARRAYCOPY = PREFIX + "JLONG_ARRAYCOPY" + POSTFIX; - static { - IRNode.callLeafNoFpOfMethodNodes(JLONG_ARRAYCOPY, InlineTypeRegexes.JLONG_DISJOINT_ARRAYCOPY); - } - - public static final String ALLOC_OF_MYVALUE_KLASS = PREFIX + "ALLOC_OF_MYVALUE_KLASS" + POSTFIX; - static { - IRNode.allocateOfNodes(ALLOC_OF_MYVALUE_KLASS, InlineTypeRegexes.MYVALUE_KLASS); - } - - public static final String ALLOC_ARRAY_OF_MYVALUE_KLASS = PREFIX + "ALLOC_ARRAY_OF_MYVALUE_KLASS" + POSTFIX; - static { - IRNode.allocateArrayOfNodes(ALLOC_ARRAY_OF_MYVALUE_KLASS, InlineTypeRegexes.MYVALUE_KLASS); - } - - // TODO: Revisit with JDK-8380875 - public static final String LOAD_OF_ANY_KLASS = PREFIX + "LOAD_OF_ANY_KLASS" + POSTFIX; - static { - String loadNode = "Load(B|UB|S|US|I|L|F|D|P|N)"; - String valueClass = "@instptr:compiler/valhalla/inlinetypes/[\\w/]*"; - String regex = START + loadNode + MID + valueClass + END; - IRNode.beforeMatching(LOAD_OF_ANY_KLASS, regex); - } - - // TODO: Revisit with JDK-8380875 - public static final String STORE_OF_ANY_KLASS = PREFIX + "STORE_OF_ANY_KLASS" + POSTFIX; - static { - IRNode.anyStoreOfNodes(STORE_OF_ANY_KLASS, InlineTypeRegexes.ANY_KLASS); - } - - // Dummy method to call to force the static initializer blocks to be run before starting the IR framework. - public static void forceStaticInitialization() {} -} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java deleted file mode 100644 index ee447bf7e11f..000000000000 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.valhalla.inlinetypes; - -public class InlineTypeRegexes { - public static final String MYVALUE_KLASS = "compiler/valhalla/inlinetypes/.*MyValue\\w*"; - public static final String ANY_KLASS = "compiler/valhalla/inlinetypes/[\\w/]*"; - public static final String STORE_INLINE_TYPE_FIELDS = "store_inline_type_fields"; - public static final String JDK_INTERNAL_MISC_UNSAFE = "# Static jdk.internal.misc.Unsafe::"; - public static final String LOAD_UNKNOWN_INLINE = "load_unknown_inline_blob \\(C2 runtime\\)"; - public static final String STORE_UNKNOWN_INLINE = "store_unknown_inline_blob \\(C2 runtime\\)"; - public static final String INLINE_ARRAY_NULL_GUARD = "null_check' action='none'"; - public static final String JLONG_DISJOINT_ARRAYCOPY = "jlong_disjoint_arraycopy"; - public static final String CHECKCAST_ARRAYCOPY = "checkcast_arraycopy"; - public static final String JAVA_LANG_OBJECT_CLONE = "java.lang.Object::clone"; -} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java index 2f49b5fe09cc..4966465665c4 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java @@ -36,24 +36,24 @@ import java.lang.reflect.Method; import java.util.Arrays; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CHECKCAST_ARRAYCOPY; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CLONE_INTRINSIC_SLOW_PATH; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.INLINE_ARRAY_NULL_GUARD; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.JLONG_ARRAYCOPY; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE; import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.ALLOC; import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY; +import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.CHECKCAST_ARRAYCOPY; import static compiler.lib.ir_framework.IRNode.CLASS_CHECK_TRAP; +import static compiler.lib.ir_framework.IRNode.CLONE_INTRINSIC_SLOW_PATH; +import static compiler.lib.ir_framework.IRNode.INLINE_ARRAY_NULL_GUARD; import static compiler.lib.ir_framework.IRNode.INTRINSIC_TRAP; +import static compiler.lib.ir_framework.IRNode.JLONG_ARRAYCOPY; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.LOOP; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; /* diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java index a37249a0e36e..7e0c3ad14937 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java @@ -112,13 +112,8 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiFunction; -import java.util.function.IntFunction; -import java.util.stream.IntStream; -import java.util.stream.Stream; import static compiler.lib.template_framework.Template.*; -import static compiler.lib.template_framework.Template.scope; public class TestArraysCopyOf { private static final RestrictableGenerator RANDOM_LENGTH = Generators.G.ints().restricted(0, 32); @@ -226,9 +221,9 @@ static interface I {} public #klass(\ """, - concat(primitiveTypes, (type, _) -> scope(type.name() + " _" + type.name())), + mapAndJoin(primitiveTypes, ", ", type -> scope(type.name() + " _" + type.name())), ") {\n", - loop(primitiveTypes, (type, _) -> scope( + map(primitiveTypes, type -> scope( let("type", type.name()), " this._#type = _#type;\n")), // Initializer @@ -238,7 +233,7 @@ static interface I {} static #klass init() { return new #klass(\ """, - concat(primitiveTypes, (type, _) -> scope(type.con())), + mapAndJoin(primitiveTypes, ", ", type -> scope(type.con())), ");\n", """ } @@ -249,7 +244,7 @@ static interface I {} static Object[] oArr = new Object[1]; """, // Passing A.class, int.class etc. Should always throw. - loop(instanceAndPrimitiveClasses.size(), i -> scope( + repeat(instanceAndPrimitiveClasses.size(), i -> scope( let("i", uniqueIndex.getAndIncrement()), let("klass", instanceAndPrimitiveClasses.get(i)), let("test", "testNonArrayClass_" + instanceAndPrimitiveClasses.get(i)), @@ -267,7 +262,7 @@ static interface I {} } """)), // Passing in primitive type arrays which like int[].class which should throw. - loop(primitiveTypeClasses.size(), i -> scope( + repeat(primitiveTypeClasses.size(), i -> scope( let("i", uniqueIndex.getAndIncrement()), let("klass", primitiveTypeClasses.get(i) + "[]"), let("test", "testPrimitiveArrayClass_" + primitiveTypeClasses.get(i)), @@ -285,7 +280,7 @@ static interface I {} } """)), // Normal tests with non-primitive type arrays. - loop(concreteInstanceClasses.size(), i -> scope( + repeat(concreteInstanceClasses.size(), i -> scope( let("i", uniqueIndex.getAndIncrement()), let("klass_name", concreteInstanceClasses.get(i).name()), let("klass", concreteInstanceClasses.get(i).name() + "[]"), @@ -312,7 +307,7 @@ static interface I {} } """)), // Normal tests with value class arrays. - loop(newValueClassArrayScopes, (init, i) -> scope( + map(newValueClassArrayScopes, (init, i) -> scope( let("i", uniqueIndex.getAndIncrement()), let("klass_name", valueClasses.get(i).name()), let("klass", valueClasses.get(i).name() + "[]"), @@ -360,28 +355,4 @@ static String generateTestMethodString() { } """; } - - static List loop(int limit, IntFunction function) { - return IntStream.range(0, limit) - .mapToObj(function) - .toList(); - } - - static List loop(List items, BiFunction function) { - return IntStream.range(0, items.size()) - .mapToObj(i -> function.apply(items.get(i), i)) - .toList(); - } - - static List concat(List items, BiFunction function) { - return IntStream.range(0, items.size()) - .boxed() - .flatMap(i -> { - ScopeToken token = function.apply(items.get(i), i); - return i == items.size() - 1 ? - Stream.of(token) : - Stream.of(token, scope(", ")); - }) - .toList(); - } -} \ No newline at end of file +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java index 6519a4191bd5..848570c04a6c 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java @@ -33,15 +33,15 @@ import jdk.internal.vm.annotation.NullRestricted; import jdk.test.whitebox.WhiteBox; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; import static compiler.valhalla.inlinetypes.InlineTypes.*; +import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.LOOP; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; import static compiler.lib.ir_framework.IRNode.SCOPE_OBJECT; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; /* diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java index 7e38e3e0092f..4453515dd959 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java @@ -32,14 +32,14 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Method; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.ALLOC; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; import static compiler.lib.ir_framework.IRNode.CALL_OF_METHOD; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; import jdk.internal.value.ValueClass; diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java new file mode 100644 index 000000000000..1238ccc37b6f --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=0-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 0:fast + */ + +/* + * @test id=1-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 1:fast + */ + +/* + * @test id=2-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 2:fast + */ + +/* + * @test id=3-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 3:fast + */ + +/* + * @test id=4-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 4:fast + */ + +/* + * @test id=5-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 5:fast + */ + +/* + * @test id=6-fast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 6:fast + */ + +/* + * @test id=0-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 0:nofast + */ + +/* + * @test id=1-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 1:nofast + */ + +/* + * @test id=2-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 2:nofast + */ + +/* + * @test id=3-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 3:nofast + */ + +/* + * @test id=4-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 4:nofast + */ + +/* + * @test id=5-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 5:nofast + */ + +/* + * @test id=6-nofast + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 6:nofast + */ + +/* + * @test id=0-fast-nointrinsics + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 0:fast:nointrinsics + */ + +/* + * @test id=0-nofast-nointrinsics + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 0:nofast:nointrinsics + */ + +/* + * @test id=1-fast-nointrinsics + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 1:fast:nointrinsics + */ + +/* + * @test id=1-nofast-nointrinsics + * @summary Test hashcode fast path with value classes + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run main ${test.main.class} 1:nofast:nointrinsics + */ + +package compiler.valhalla.inlinetypes; + +import static compiler.lib.generators.Generators.G; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +import static compiler.lib.ir_framework.IRNode.*; + +public class TestHashcodeFastPath { + record RunSettings(int scenario, boolean useHashCodeFastPath, boolean disableIntrinsics) {} + + static boolean parseBooleanSetting(String[] pieces, int idx, String false_str, String true_str, boolean def) { + if (pieces.length > idx) { + String piece = pieces[idx]; + if(piece.equals(false_str)) { + return false; + } else if(piece.equals(true_str)) { + return true; + } else { + throw new RuntimeException("Unknown setting: " + piece); + } + } else { + return def; + } + } + + static RunSettings parseSetting(String arg) { + String[] pieces = arg.split(":"); + + int scenario = Integer.parseInt(pieces[0]); + + boolean useHashCodeFastPath = parseBooleanSetting(pieces, 1, "nofast", "fast", true); + boolean disableIntrinsics = parseBooleanSetting(pieces, 2, "intrinsics", "nointrinsics", false); + + return new RunSettings(scenario, useHashCodeFastPath, disableIntrinsics); + } + + public static void main(String[] args) { + Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; + RunSettings settings = parseSetting(args[0]); + Scenario scenario = scenarios[settings.scenario]; + if (!settings.useHashCodeFastPath) { + scenario.addFlags("-XX:-UseHashcodeFastPath"); + } + if (settings.disableIntrinsics) { + scenario.addFlags("-XX:DisableIntrinsic=_identityHashCode"); + } + scenario.addFlags("-XX:CompileCommand=exclude,*::h"); + InlineTypes.getFramework() + .addScenarios(scenario) + .start(); + } + + static abstract value class UniquelyDerivedBase { + } + static value class UniqueDerived extends UniquelyDerivedBase { + byte b; + UniqueDerived(byte b) { + this.b = b; + } + } + + static abstract value class MultiplyDerivedBase { + } + static value class Derived extends MultiplyDerivedBase { + byte b; + Derived(byte b) { + this.b = b; + } + } + // Prevents Derived from being the only concrete class under MultiplyDerivedBase + static value class EvilDerived extends MultiplyDerivedBase { + byte b; + EvilDerived(byte b) { + this.b = b; + } + } + + static value class DerivedWrapper { + short s; + Derived b; + DerivedWrapper(byte b, short s) { + this.b = new Derived(b); + this.s = s; + } + } + + value record ShortWrapper(short s) {} + + static abstract value class AbstractShort { + ShortWrapper s; + AbstractShort(int s) { + this.s = new ShortWrapper((short)s); + } + + public String toString() { + return "AbstractShort(" + s + ")"; + } + } + + static value class ShortWithInt extends AbstractShort { + int i; + ShortWithInt(int s, int i) { + this.i = i; + super(s); + } + public String toString() { + return "ShortWithInt(s=" + s.s + ", i=" + i + ")"; + } + } + + static value class Empty {} + + + + static value class LongLong { + long s; + long b; + LongLong(long s, long b) { + this.s = s; + this.b = b; + } + } + static value class WithOop { + String s; + WithOop(String s) { + this.s = s; + } + } + + int h(Object o) { + return System.identityHashCode(o); + } + + @Run(test = { + "h_object", + "h_unique_derived", + "h_uniquely_derived_base", + "h_derived", + "h_base", + "h_derived_hidden_type", + "h_short_with_int", + "h_short_with_int_hidden_type", + "h_with_oop", + "h_with_oop_hidden_type", + }) + @Warmup(0) // We want to prevent profiling + public void run() { + var wrapper = new DerivedWrapper((byte)0, (short)0xa2a1); + var wrapper_ = new DerivedWrapper((byte)0, (short)0xa2a1); + + var derived1 = new Derived((byte)0); + var derived_ = new Derived((byte)0); + var evilDerived1 = new EvilDerived((byte)1); // Force class loading + + Asserts.assertEQ(h_object(null), h(null)); + Asserts.assertEQ(h_object(null), 0); + Asserts.assertEQ(h_object(wrapper), h(wrapper_)); + + Asserts.assertEQ(h_derived(derived1), h(derived_)); + Asserts.assertEQ(h_base(derived1), h(derived_)); + Asserts.assertEQ(h_derived_hidden_type(derived1), h(derived_)); + + Asserts.assertEQ(h_base(evilDerived1), h(evilDerived1)); + + var uniqueDerived = new UniqueDerived((byte)0); + var uniqueDerived_ = new UniqueDerived((byte)0); + + Asserts.assertEQ(h_object(uniqueDerived), h(uniqueDerived_)); + Asserts.assertEQ(h_unique_derived(uniqueDerived), h(uniqueDerived_)); + Asserts.assertEQ(h_uniquely_derived_base(uniqueDerived), h(uniqueDerived_)); + + var swi = new ShortWithInt(0, 1); + var swi_ = new ShortWithInt(0, 1); + Asserts.assertEQ(h_object(swi), h(swi_)); + Asserts.assertEQ(h_short_with_int(swi), h(swi_)); + Asserts.assertEQ(h_short_with_int_hidden_type(swi), h(swi_)); + + var empty = new Empty(); + var empty_ = new Empty(); + Asserts.assertEQ(h_object(empty), h(empty_)); + + var with_oops = new WithOop("a"); + var with_oops_ = new WithOop("a"); + Asserts.assertEQ(h_with_oop(with_oops), h(with_oops_)); + Asserts.assertEQ(h_with_oop_hidden_type(with_oops), h(with_oops_)); + } + + static final String IDENTITY_HASHCODE = "identityHashCode"; + + static final int URSHIFT_L_COUNT_FOR_CACHE_PATH = 1; // Shift object header + static final int URSHIFT_L_COUNT_FOR_FAST_PATH = 2; // Shift class header, maybe shift again for long payload + static final int RSHIFT_L_COUNT_FOR_FAST_PATH = 1; // Shift object payload + static final String CACHE_PATH_U = "" + URSHIFT_L_COUNT_FOR_CACHE_PATH; + static final String CACHE_AND_FAST_PATH_U = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + URSHIFT_L_COUNT_FOR_FAST_PATH); + static final String FAST_PATH_S = "" + RSHIFT_L_COUNT_FOR_FAST_PATH; + + // Get hashcode fast path + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + int h_object(Object a) { + return System.identityHashCode(a); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_unique_derived(UniqueDerived a) { + return System.identityHashCode(a); + } + + // No hashcode fast path: single concrete derived, and the call will be intrinsified + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_uniquely_derived_base(UniquelyDerivedBase a) { + return System.identityHashCode(a); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_derived(Derived a) { + return System.identityHashCode(a); + } + + // Hashcode fast path is generated, the type is not precise enough for intrinsifying + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + int h_base(MultiplyDerivedBase a) { + return System.identityHashCode(a); + } + + // Hides the type during parsing when always incrementally inlining + @ForceInline + public Object getter(Object o) { + return o; + } + + // With late inlining, type is hidden at first, and a fast path is generated. + // Later, type becomes precise, call is intrinsified and fast path is removed. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_derived_hidden_type(Derived a) { + return System.identityHashCode(getter(a)); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified. Fast path wouldn't work anyway because it has a weird size. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL},failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_short_with_int(ShortWithInt a) { + return System.identityHashCode(a); + } + + // With late inlining, type is hidden at first, and a fast path is generated. + // Later, type becomes precise, call is intrinsified and fast path is removed. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_short_with_int_hidden_type(ShortWithInt a) { + return System.identityHashCode(getter(a)); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified if possible. Fast path wouldn't work anyway because of the oop. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_with_oop(WithOop a) { + return System.identityHashCode(a); + } + + // With late inlining, type is hidden at first, and a fast path is generated. + // Later, type becomes precise, call would be intrinsified if possible. But it's not. Yet, we can also find out the fast path won't work, and it is removed. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_with_oop_hidden_type(WithOop a) { + return System.identityHashCode(getter(a)); + } + + // Only null path should exist + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_null() { + return System.identityHashCode(null); + } + + // Only null path should survive + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_null_hidden_type() { + return System.identityHashCode(getter(null)); + } + + @Run(test = { + "h_byte", + "h_short", + "h_int", + "h_long", + "h_long_long", + "h_short_with_int2", + "h_short_with_int_hidden_type2", + "h_with_oop2", + }) + public void run2() { + for (int i = Byte.MIN_VALUE; i<= Byte.MAX_VALUE; ++i) { + Asserts.assertEQ(h_byte(new Byte((byte)i)), h(new Byte((byte)i)), "i = " + i); + } + // -1000 and 1000 are here to have a "normal" range, but outside what Short, + // Integer or Long will cache, that is without cached hash in the header. + int HALF_WIDTH = 256; + for (short base : new short[]{0, -1000, 1000, Short.MIN_VALUE, Short.MAX_VALUE}) { + for (short k = 0; k < 2 * HALF_WIDTH + 1; ++k) { + short s = (short) (k + base - HALF_WIDTH); + Asserts.assertEQ(h_short(new Short(s)), h(new Short(s)), "s = " + s); + } + } + for (int base : new int[]{0, -1000, 1000, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}) { + for (int k = 0; k < 2 * HALF_WIDTH + 1; ++k) { + int i = k + base - HALF_WIDTH; + Asserts.assertEQ(h_int(new Integer(i)), h(new Integer(i)), "i = " + i); + Asserts.assertEQ(h_short_with_int2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i); + Asserts.assertEQ(h_short_with_int_hidden_type2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i); + } + } + for (long base : new long[]{0, -1000, 1000, Long.MIN_VALUE, Long.MAX_VALUE}) { + for (long k = 0; k < 2 * HALF_WIDTH + 1; ++k) { + long l = k + base - HALF_WIDTH; + Asserts.assertEQ(h_long(new Long(l)), h(new Long(l)), "l = " + l); + Asserts.assertEQ(h_long_long(new LongLong(l, l)), h(new LongLong(l, l)), "l = " + l); + Asserts.assertEQ(h_long_long(new LongLong((l << 32L) + l, Long.MAX_VALUE - l)), h(new LongLong((l << 32L) + l, Long.MAX_VALUE - l)), "l = " + l); + String str = String.valueOf(l); + Asserts.assertEQ(h_with_oop2(new WithOop(str)), h(new WithOop(str)), "l = " + l); + + Long l_ = new Long(l); + int expected_hash = h(l_); + Asserts.assertEQ(h_long(l_), expected_hash, "l = " + l); + } + } + + short s = G.ints().next().shortValue(); + Asserts.assertEQ(h_short(new Short(s)), h(new Short(s)), "s = " + s); + int i = G.ints().next(); + Asserts.assertEQ(h_int(new Integer(i)), h(new Integer(i)), "i = " + i); + long l = G.longs().next(); + Asserts.assertEQ(h_long(new Long(l)), h(new Long(l)), "l = " + l); + Asserts.assertEQ(h_long_long(new LongLong(i, i)), h(new LongLong(i, i)), "i = " + i); + Asserts.assertEQ(h_short_with_int2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i); + Asserts.assertEQ(h_short_with_int_hidden_type2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i); + String str = String.valueOf(i); + Asserts.assertEQ(h_with_oop2(new WithOop(str)), h(new WithOop(str)), "i = " + i); + + Long lon = new Long(i); + int expected_hash = h(lon); + Asserts.assertEQ(h_long(lon), expected_hash, "lon = " + lon); + } + + // Statically expanded + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_byte(Byte a) { + return System.identityHashCode(a); + } + // Statically expanded + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_short(Short a) { + return System.identityHashCode(a); + } + // Statically expanded + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_int(Integer a) { + return System.identityHashCode(a); + } + // Statically expanded + public static final String ONE_LONG_IN_INTRINSIC = "" + 1; + public static final String CACHE_PATH_AND_ONE_LONG_IN_INTRINSIC = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + 1); + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_AND_ONE_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, ONE_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_long(Long a) { + return System.identityHashCode(a); + } + // Statically expanded + public static final String TWO_LONG_IN_INTRINSIC = "" + 2; + public static final String CACHE_PATH_AND_TWO_LONG_IN_INTRINSIC = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + 2); + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_AND_TWO_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, TWO_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_long_long(LongLong a) { + return System.identityHashCode(a); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified. Fast path wouldn't work anyway because it has a weird size. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_short_with_int2(ShortWithInt a) { + return System.identityHashCode(a); + } + + // With late inlining, type is hidden at first, and a fast path is generated. + // Later, type becomes precise, call is intrinsified and fast path is removed. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_short_with_int_hidden_type2(ShortWithInt a) { + return System.identityHashCode(getter(a)); + } + + // No hashcode fast path: the type is precise, and the call will be intrinsified if possible. Fast path wouldn't work anyway because of the oop. + @Test + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""}) + @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"}) + int h_with_oop2(WithOop a) { + return System.identityHashCode(a); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java index b2b81b92eceb..23b42bad191b 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java @@ -36,13 +36,13 @@ import jdk.internal.vm.annotation.NullRestricted; import jdk.test.whitebox.WhiteBox; -import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CALL_UNSAFE; import static compiler.valhalla.inlinetypes.InlineTypes.rI; import static compiler.valhalla.inlinetypes.InlineTypes.rL; +import static compiler.lib.ir_framework.IRNode.CALL_UNSAFE; import static compiler.lib.ir_framework.IRNode.LOAD; import static compiler.lib.ir_framework.IRNode.LOAD_KLASS; +import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD; /* * @test diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java index c049a8548fe8..513ee0c20644 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java @@ -39,27 +39,27 @@ import jdk.internal.vm.annotation.LooselyConsistentValue; import jdk.internal.vm.annotation.NullRestricted; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.INLINE_ARRAY_NULL_GUARD; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE; import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.ALLOC; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; import static compiler.lib.ir_framework.IRNode.CLASS_CHECK_TRAP; import static compiler.lib.ir_framework.IRNode.COUNTED_LOOP; import static compiler.lib.ir_framework.IRNode.COUNTED_LOOP_MAIN; import static compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD; import static compiler.lib.ir_framework.IRNode.FIELD_ACCESS; +import static compiler.lib.ir_framework.IRNode.INLINE_ARRAY_NULL_GUARD; import static compiler.lib.ir_framework.IRNode.LOAD; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.LOAD_P; +import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.LOOP; import static compiler.lib.ir_framework.IRNode.MEMBAR; import static compiler.lib.ir_framework.IRNode.NULL_CHECK_TRAP; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; /* diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java index 3ff5a0c86f59..a45ab06e49c8 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java @@ -34,8 +34,6 @@ import jdk.internal.vm.annotation.NullRestricted; import static compiler.lib.ir_framework.IRNode.*; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE; import static compiler.valhalla.inlinetypes.InlineTypes.*; /* diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java index 9ad94fc5b728..150e0baaedec 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java @@ -34,10 +34,11 @@ import jdk.internal.vm.annotation.NullRestricted; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_INLINE_FIELDS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_INLINE_FIELDS; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; + import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.STATIC_CALL; diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java index 4faf637f99f7..9f8242cdd9e9 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java @@ -26,19 +26,19 @@ import jdk.test.lib.Asserts; import compiler.lib.ir_framework.*; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE; import static compiler.valhalla.inlinetypes.InlineTypes.rI; import static compiler.valhalla.inlinetypes.InlineTypes.rL; import static compiler.valhalla.inlinetypes.InlineTypes.rD; import static compiler.lib.ir_framework.IRNode.ALLOC; +import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.LOOP; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; import jdk.internal.value.ValueClass; diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java index 61d923ed629b..43446ec688d2 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java @@ -38,15 +38,15 @@ import jdk.internal.vm.annotation.LooselyConsistentValue; import jdk.internal.vm.annotation.NullRestricted; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.ALLOC; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; import static compiler.lib.ir_framework.IRNode.CMP_N; import static compiler.lib.ir_framework.IRNode.CMP_P; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; /* diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java index e6b647b6dfba..cdc1bc09e54f 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java @@ -30,9 +30,10 @@ import jdk.internal.vm.annotation.LooselyConsistentValue; import jdk.internal.vm.annotation.NullRestricted; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; + import static compiler.valhalla.inlinetypes.InlineTypes.rI; import static compiler.valhalla.inlinetypes.InlineTypes.rL; diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java index 054d8b63e699..0287e6b4b333 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java @@ -51,8 +51,7 @@ import compiler.lib.template_framework.ScopeToken; import compiler.lib.template_framework.Template; -import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.*; public class TestScalarizedCallingConventionLimits { private static final String GENERATED_CLASS_NAME = "GeneratedScalarizedCallingConventionLimits"; @@ -89,7 +88,7 @@ static int hash(Object... values) { """, // Generate interfaces with value and identity-class implementations and a method // with a varying number of arguments to stress test the calling convention. - loop(MAX_ARGUMENT_COUNT, argumentCount -> scope( + repeat(MAX_ARGUMENT_COUNT, argumentCount -> scope( let("argumentCount", argumentCount), let("classname", "ValueImpl" + argumentCount), let("parameters", commaSeparated(argumentCount, i -> "Integer a" + i)), @@ -138,19 +137,19 @@ public int m(#parameters) { """)), // Generate methods with a value class receiver with a varying number of oop fields to stress // test code buffers during nmethod entry point generation (oops need GC barriers etc.) - loop(MAX_OOP_RECEIVER_FIELD_COUNT, fieldCount -> scope( + repeat(MAX_OOP_RECEIVER_FIELD_COUNT, fieldCount -> scope( let("fieldCount", fieldCount), let("arguments", commaSeparated(fieldCount, i -> "f" + i)), """ static value class OopReceiver#fieldCount { """, - loop(fieldCount, i -> scope( + repeat(fieldCount, i -> scope( " Object f" + i + ";\n")), """ OopReceiver#fieldCount(Object[] values) { """, - loop(fieldCount, i -> scope( + repeat(fieldCount, i -> scope( " this.f" + i + " = values[" + i + "];\n")), """ } @@ -176,9 +175,9 @@ int m() { """ public static void run() { """, - loop(MAX_ARGUMENT_COUNT, i -> scope( + repeat(MAX_ARGUMENT_COUNT, i -> scope( " test" + i + "();\n")), - loop(MAX_OOP_RECEIVER_FIELD_COUNT, i -> scope( + repeat(MAX_OOP_RECEIVER_FIELD_COUNT, i -> scope( " testOopReceiver" + i + "();\n")), """ } @@ -190,11 +189,5 @@ public static void run() { private static String commaSeparated(int count, IntFunction element) { return IntStream.range(0, count).mapToObj(element).collect(Collectors.joining(", ")); } - - private static List loop(int limit, IntFunction function) { - return IntStream.range(0, limit) - .mapToObj(function) - .toList(); - } } diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java index 7ab9f1b710be..ebf246cd7d60 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java @@ -28,11 +28,11 @@ import java.lang.reflect.Method; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS; -import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS; import static compiler.valhalla.inlinetypes.InlineTypes.*; import static compiler.lib.ir_framework.IRNode.ALLOC; +import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS; +import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS; import jdk.internal.vm.annotation.LooselyConsistentValue; import jdk.internal.vm.annotation.NullRestricted; diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java new file mode 100644 index 000000000000..41c240d159ae --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8389187 + * @summary Verify stack chunks with extended compiled frames + * @enablePreview + * @requires vm.debug == true & vm.continuations + * @library /test/lib + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+VerifyContinuations TestVirtualThreadExtendedFrame + */ + +public class TestVirtualThreadExtendedFrame { + static value class V { + int a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0; + } + + static volatile int sink; + + static void recurse(V value, int depth, boolean park) { + if (depth > 0) { + recurse(value, depth - 1, park); + } else if (park) { + sink = value.a0; + Thread.yield(); + } + } + + public static void main(String[] args) throws Exception { + for (int i = 0; i < 10_000; i++) { + recurse(new V(), 2, false); + } + Thread thread = Thread.startVirtualThread(() -> recurse(new V(), 2, true)); + thread.join(); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java new file mode 100644 index 000000000000..5a2bc9985493 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java @@ -0,0 +1,61 @@ +/* + * 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 8388596 + * @summary Test freezing a c2 compiled MethodHandle target frame with a value class argument + * @enablePreview + * @requires vm.continuations + * @library /test/lib + * @run main/othervm -XX:-Inline -Xbatch -XX:-TieredCompilation TestVirtualThreadMethodHandle + */ + +import java.lang.invoke.*; + +import jdk.test.lib.Asserts; + +public class TestVirtualThreadMethodHandle { + static value class V { int a=0, b=0, c=0, d=0, e=0, f=0, g=0; } + static boolean failed; + + static MethodHandle MH; + + static void target(V v) { Thread.yield(); } + + static void run() { + try { + for (int n = 0; n < 20_000; n++) MH.invokeExact(new V()); + } catch (Throwable t) { + failed = true; + throw new RuntimeException("MethodHandle invocation failed", t); + } + } + + public static void main(String[] args) throws Exception { + MH = MethodHandles.lookup().findStatic(TestVirtualThreadMethodHandle.class, "target", MethodType.methodType(void.class, V.class)); + run(); + Thread.startVirtualThread(TestVirtualThreadMethodHandle::run).join(); + Asserts.assertFalse(failed, "MethodHandle invocation failed"); + } +} diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java index 3607459a5baf..f66ab190a27a 100644 --- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java +++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java @@ -204,6 +204,20 @@ * compiler.valhalla.inlinetypes.TestVirtualThreads 50000 */ +/* + * @test id=verify-cont + * @key randomness + * @summary Test that Virtual Threads work well with Value Objects. + * @library /test/lib /compiler/whitebox / + * @enablePreview + * @requires vm.debug == true & vm.continuations + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+VerifyContinuations + * compiler.valhalla.inlinetypes.TestVirtualThreads + */ + package compiler.valhalla.inlinetypes; import java.lang.reflect.Method; diff --git a/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java b/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java index 2c5b74a078ea..b95771d98629 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java @@ -43,7 +43,7 @@ * @key randomness * @library /test/lib / * @requires vm.compiler2.enabled - * @requires (os.simpleArch == "aarch64" & vm.cpu.features ~= ".*asimd.*") | (os.simpleArch == "riscv64" & vm.cpu.features ~= ".*zvbb.*") + * @requires (os.simpleArch == "aarch64" & vm.cpu.features ~= ".*asimd.*") | (os.simpleArch == "riscv64" & vm.cpu.features ~= ".*rvv.*") * @summary AArch64: [vector] Make all bits set vector sharable for match rules * @modules jdk.incubator.vector * @@ -94,7 +94,7 @@ public class AllBitsSetVectorMatchRuleTest { // Tests of C2 match rules for vector ops containing an all-bits-set vector operand. @Test - @IR(counts = { IRNode.VAND_NOT_I, " >= 1" }) + @IR(counts = { IRNode.VAND_NOT_I, " >= 1" }, applyIfCPUFeatureOr = {"asimd", "true", "zvbb", "true"}) public static void testAllBitsSetVector() { IntVector av = IntVector.fromArray(I_SPECIES, ia, 0); IntVector bv = IntVector.fromArray(I_SPECIES, ib, 0); @@ -107,7 +107,7 @@ public static void testAllBitsSetVector() { } @Test - @IR(counts = { IRNode.VAND_NOT_L, " >= 1" }) + @IR(counts = { IRNode.VAND_NOT_L, " >= 1" }, applyIfCPUFeatureOr = {"asimd", "true", "zvbb", "true"}) public static void testVectorVAndNotL() { LongVector av = LongVector.fromArray(L_SPECIES, la, 0); LongVector bv = LongVector.fromArray(L_SPECIES, lb, 0); @@ -120,8 +120,7 @@ public static void testVectorVAndNotL() { } @Test - @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfCPUFeature = {"sve", "true"}) - @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfCPUFeatureOr = {"sve", "true", "zvbb", "true"}) public static void testVectorVAndNotIMasked() { VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0); IntVector av = IntVector.fromArray(I_SPECIES, ia, 0); @@ -137,8 +136,7 @@ public static void testVectorVAndNotIMasked() { } @Test - @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfCPUFeature = {"sve", "true"}) - @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfCPUFeatureOr = {"sve", "true", "zvbb", "true"}) public static void testVectorVAndNotLMasked() { VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0); LongVector av = LongVector.fromArray(L_SPECIES, la, 0); @@ -154,7 +152,7 @@ public static void testVectorVAndNotLMasked() { } @Test - @IR(counts = { IRNode.RISCV_VAND_NOTI_VX, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.RISCV_VAND_NOTI_VX, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"}) public static void testAllBitsSetVectorRegI() { IntVector av = IntVector.fromArray(I_SPECIES, ia, 0); int bs = ib[0]; @@ -167,7 +165,7 @@ public static void testAllBitsSetVectorRegI() { } @Test - @IR(counts = { IRNode.RISCV_VAND_NOTL_VX, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.RISCV_VAND_NOTL_VX, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"}) public static void testAllBitsSetVectorRegL() { LongVector av = LongVector.fromArray(L_SPECIES, la, 0); long bs = lb[0]; @@ -180,7 +178,7 @@ public static void testAllBitsSetVectorRegL() { } @Test - @IR(counts = { IRNode.RISCV_VAND_NOTI_VX_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.RISCV_VAND_NOTI_VX_MASKED, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"}) public static void testAllBitsSetVectorRegIMask() { VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0); IntVector av = IntVector.fromArray(I_SPECIES, ia, 0); @@ -196,7 +194,7 @@ public static void testAllBitsSetVectorRegIMask() { } @Test - @IR(counts = { IRNode.RISCV_VAND_NOTL_VX_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"}) + @IR(counts = { IRNode.RISCV_VAND_NOTL_VX_MASKED, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"}) public static void testAllBitsSetVectorRegLMask() { VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0); LongVector av = LongVector.fromArray(L_SPECIES, la, 0); @@ -214,7 +212,7 @@ public static void testAllBitsSetVectorRegLMask() { // Tests that VectorMask.andNot() chains match to VMASK_AND_NOT / VAND_NOT (two andNot ops). @Test @IR(counts = { IRNode.VAND_NOT_I, "2" }, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) - @IR(counts = { IRNode.VMASK_AND_NOT_I, "2" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_I, "2" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testMaskAndNotI() { VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0); @@ -229,7 +227,7 @@ public static void testMaskAndNotI() { @Test @IR(counts = { IRNode.VAND_NOT_L, "2" }, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) - @IR(counts = { IRNode.VMASK_AND_NOT_L, "2" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_L, "2" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testMaskAndNotL() { VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0); @@ -244,7 +242,7 @@ public static void testMaskAndNotL() { // Tests that mask.not().and(other) matches to VMASK_AND_NOT (AndVMask commutative rule). @Test - @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testCommutativeAndVMaskI() { VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0); @@ -257,7 +255,7 @@ public static void testCommutativeAndVMaskI() { } @Test - @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testCommutativeAndVMaskL() { VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0); @@ -271,7 +269,7 @@ public static void testCommutativeAndVMaskL() { // Tests that mask.and(allTrue.xor(other)) matches to VMASK_AND_NOT (XorVMask commutative rule). @Test - @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testCommutativeXorVMaskI() { VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0); @@ -285,7 +283,7 @@ public static void testCommutativeXorVMaskI() { } @Test - @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeature = {"sve", "true"}) + @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"}) public static void testCommutativeXorVMaskL() { VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0); VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0); diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java new file mode 100644 index 000000000000..789c0c71a4bd --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.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. + */ + +/* + * @test + * @bug 8390625 + * @summary Test VectorRearrange with the same source and shuffle operand + * @requires vm.compiler2.enabled + * @modules jdk.incubator.vector + * @library /test/lib + * @run main/othervm -Xbatch ${test.main.class} + */ + +package compiler.vectorapi; + +import jdk.incubator.vector.ShortVector; +import jdk.test.lib.Asserts; + +public class TestSelectFromSameOperand { + private static final short[] INPUT = {7, 6, 5, 4, 3, 2, 1, 0}; + private static final short[] OUTPUT = new short[INPUT.length]; + + public static void test() { + // The already masked 'vector' becomes both source and shuffle of the VectorRearrange node + ShortVector vector = ShortVector.fromArray(ShortVector.SPECIES_128, INPUT, 0).and((short) 7); + vector.selectFrom(vector).intoArray(OUTPUT, 0); + } + + public static void main(String[] args) { + for (int i = 0; i < 100_000; i++) { + test(); + } + for (int i = 0; i < OUTPUT.length; i++) { + Asserts.assertEQ(OUTPUT[i], (short) i); + } + } +} + diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java index cfa51b9434c1..94c995f3f687 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8387594 + * @bug 8387594 8390044 * @key randomness * @library /test/lib / * @summary IR tests for Vector API lanewise DIV @@ -111,7 +111,7 @@ public class VectorDivTest { @Test @IR(counts = { IRNode.DIV_VB, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testDivByte() { ByteVector va = ByteVector.fromArray(B_SPECIES, ba, 0); ByteVector vb = ByteVector.fromArray(B_SPECIES, bb, 0); @@ -120,7 +120,7 @@ public static void testDivByte() { @Test @IR(counts = { IRNode.DIV_VS, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testDivShort() { ShortVector va = ShortVector.fromArray(S_SPECIES, sa, 0); ShortVector vb = ShortVector.fromArray(S_SPECIES, sb, 0); @@ -129,7 +129,7 @@ public static void testDivShort() { @Test @IR(counts = { IRNode.DIV_VI, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testDivInt() { IntVector va = IntVector.fromArray(I_SPECIES, ia, 0); IntVector vb = IntVector.fromArray(I_SPECIES, ib, 0); @@ -138,7 +138,7 @@ public static void testDivInt() { @Test @IR(counts = { IRNode.DIV_VL, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testDivLong() { LongVector va = LongVector.fromArray(L_SPECIES, la, 0); LongVector vb = LongVector.fromArray(L_SPECIES, lb, 0); @@ -165,11 +165,13 @@ public static void testDivDouble() { // Masked lanewise DIV. On AArch64, BYTE/SHORT have no native predicated // divide, so they are lowered to an unpredicated divide combined with a - // VectorBlend. + // VectorBlend. RVV has native predicated integer division for all element + // sizes. @Test - @IR(counts = { IRNode.DIV_VB, ">= 1", - IRNode.VECTOR_BLEND_B, ">= 1" }, + @IR(counts = { IRNode.DIV_VB, ">= 1" }, + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_BLEND_B, ">= 1" }, applyIfCPUFeature = { "sve", "true" }) public static void testMaskedDivByte() { VectorMask mask = VectorMask.fromArray(B_SPECIES, mask_arr, 0); @@ -179,8 +181,9 @@ public static void testMaskedDivByte() { } @Test - @IR(counts = { IRNode.DIV_VS, ">= 1", - IRNode.VECTOR_BLEND_S, ">= 1" }, + @IR(counts = { IRNode.DIV_VS, ">= 1" }, + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) + @IR(counts = { IRNode.VECTOR_BLEND_S, ">= 1" }, applyIfCPUFeature = { "sve", "true" }) public static void testMaskedDivShort() { VectorMask mask = VectorMask.fromArray(S_SPECIES, mask_arr, 0); @@ -191,7 +194,7 @@ public static void testMaskedDivShort() { @Test @IR(counts = { IRNode.DIV_VI, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testMaskedDivInt() { VectorMask mask = VectorMask.fromArray(I_SPECIES, mask_arr, 0); IntVector va = IntVector.fromArray(I_SPECIES, ia, 0); @@ -201,7 +204,7 @@ public static void testMaskedDivInt() { @Test @IR(counts = { IRNode.DIV_VL, ">= 1" }, - applyIfCPUFeature = { "sve", "true" }) + applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" }) public static void testMaskedDivLong() { VectorMask mask = VectorMask.fromArray(L_SPECIES, mask_arr, 0); LongVector va = LongVector.fromArray(L_SPECIES, la, 0); diff --git a/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java b/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java new file mode 100644 index 000000000000..76a0482322a9 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package gc; + +/** + * @test id=Serial + * @bug 8389096 + * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM. + * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These + * garbage collection should not cause hangs. + * @requires vm.flagless + * @requires vm.debug + * @requires vm.gc.Serial + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseSerialGC + */ + +/** + * @test id=Parallel + * @bug 8389096 + * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM. + * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These + * garbage collection should not cause hangs. + * @requires vm.flagless + * @requires vm.debug + * @requires vm.gc.Parallel + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseParallelGC + */ + +/** + * @test id=G1 + * @bug 8389096 + * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM. + * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These + * garbage collection should not cause hangs. + * @requires vm.flagless + * @requires vm.debug + * @requires vm.gc.G1 + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseG1GC + */ + +/** + * @test id=Z + * @bug 8389096 + * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM. + * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These + * garbage collection should not cause hangs. + * @requires vm.flagless + * @requires vm.debug + * @requires vm.gc.Z + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseZGC + */ + +/** + * @test id=Shenandoah + * @bug 8389096 + * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM. + * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These + * garbage collection should not cause hangs. + * @requires vm.flagless + * @requires vm.debug + * @requires vm.gc.Shenandoah + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseShenandoahGC + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestGCALotAtAllSafepoints { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args[0], + "-Xmx16m", + "-XX:+GCALotAtAllSafepoints", + "-XX:+ScavengeALot", + "NoSuchClass"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldMatch("Error: Could not find or load main class NoSuchClass"); + output.shouldHaveExitValue(1); + } +} diff --git a/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java b/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java new file mode 100644 index 000000000000..678829c33e71 --- /dev/null +++ b/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java @@ -0,0 +1,78 @@ +/* + * 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 TestZAllocationSpikeTolerance + * @bug 8389866 + * @summary Verifies TestZAllocationSpikeTolerance range + * - fails gracefully for invalid value + * - succeeds for valid value + * @library /test/lib + * @requires vm.gc.Z + * @run driver gc.z.TestZAllocationSpikeTolerance + */ +package gc.z; + +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class TestZAllocationSpikeTolerance { + public static void main(String[] args) throws Exception { + testInvalidValue(); + testValidValue(); + } + + private static void testInvalidValue() throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UseZGC", + "-XX:ZAllocationSpikeTolerance=-1", // invalid range + "-version" + ); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + // Ensure no crash (no assert failure) + output.shouldNotContain("assert"); + + // Expected graceful error output + output.shouldContain("ZAllocationSpikeTolerance"); + output.shouldContain("outside the allowed range"); + output.shouldContain("Error: A fatal exception has occurred. Program will exit."); + + // Graceful exit with error code 1 + output.shouldHaveExitValue(1); + } + + private static void testValidValue() throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+UseZGC", + "-XX:ZAllocationSpikeTolerance=1", // valid range + "-version" + ); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java b/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java index 95952b2cf997..774d46fd6721 100644 --- a/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java +++ b/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java @@ -122,8 +122,7 @@ static class Scenario { static final int BATCH_SIZE = 10_000; static int incrementIndex(int idx, int n) { - idx += INCREMENTS[n]; - if (idx >= N_PRECOMPUTED) idx -= N_PRECOMPUTED; + idx = (idx + INCREMENTS[n]) % N_PRECOMPUTED; return idx; } diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java index 58a90f8ba544..cc55e697a6f6 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java @@ -66,26 +66,6 @@ * @run driver VMOptionWarning Develop */ -/* @test VMOptionWarningCompactObjectHeaders - * @bug 8360700 - * @summary Warn if -XX:+UseCompactObjectHeaders is used with -XX:-UseObjectMonitorTable - * @requires vm.flagless - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run driver VMOptionWarning CompactObjectHeaders - */ - -/* @test VMOptionWarningUseObjectMonitorTable - * @bug 8360700 - * @summary Warn if -XX:-UseObjectMonitorTable is used without -XX:-UseCompactObjectHeaders - * @requires vm.flagless - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run driver VMOptionWarning UseObjectMonitorTable - */ - import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.Platform; @@ -127,20 +107,6 @@ public static void main(String[] args) throws Exception { output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM."); break; } - case "CompactObjectHeaders": { - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseCompactObjectHeaders", "-XX:+UnlockDiagnosticVMOptions", "-XX:-UseObjectMonitorTable", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - output.shouldContain("warning: -UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable"); - break; - } - case "UseObjectMonitorTable": { - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:-UseObjectMonitorTable", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - output.shouldContain("warning: -UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable"); - break; - } default: { throw new RuntimeException("Invalid argument: " + args[0]); } diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java index a8c03e259fc8..176d757dff25 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java @@ -50,7 +50,6 @@ private static void test(long forceAddress, boolean COH, long classSpaceSize, lo "-XX:-UseCompressedOops", // keep VM from optimizing heap location "-XX:+UnlockExperimentalVMOptions", "-XX:" + (COH ? "+" : "-") + "UseCompactObjectHeaders", - "-XX:" + (COH ? "+" : "-") + "UseObjectMonitorTable", "-XX:CompressedClassSpaceBaseAddress=" + forceAddress, "-XX:CompressedClassSpaceSize=" + classSpaceSize, "-Xmx64m", diff --git a/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java b/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java index 6af1602e3380..fd6ece349a24 100644 --- a/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java +++ b/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,7 @@ * @summary A collection of small tests using synchronized, wait, notify to try * and achieve good cheap coverage of UseObjectMonitorTable. * @library /test/lib - * @run main/othervm -XX:+UnlockDiagnosticVMOptions - * -XX:+UseObjectMonitorTable - * UseObjectMonitorTableTest + * @run main/othervm UseObjectMonitorTableTest */ /** @@ -37,7 +35,6 @@ * @library /test/lib * @run main/othervm -XX:+UnlockDiagnosticVMOptions * -XX:GuaranteedAsyncDeflationInterval=1 - * -XX:+UseObjectMonitorTable * UseObjectMonitorTableTest */ diff --git a/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java b/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java index a242d3afcd72..75c0fe0c8ffa 100644 --- a/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java +++ b/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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 @@ -63,7 +63,7 @@ public static void main(java.lang.String[] unused) { try { // Retrieve the class of throwable.backtrace[0][0]. - Class class2 = ((Object[]) ((Object[]) backtrace)[2])[0].getClass(); + Class class2 = ((Object[]) ((Object[]) backtrace)[1])[0].getClass(); // Segfault occurs while executing this line, to retrieve the name of // this class. diff --git a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java index 5870b4cc36c6..c1e21db4c69d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java @@ -27,7 +27,6 @@ * @bug 8290417 * @summary CDS cannot archive lambda proxy with useImplMethodHandle * @requires vm.cds - * @requires vm.cds.supports.aot.class.linking * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build pkg1.BaseWithProtectedMethod * @build pkg2.Child @@ -44,11 +43,10 @@ public class LambdaWithUseImplMethodHandle { // See pkg2/Child.jcod for details about the condition that triggers JDK-8290417 public static void main(String[] args) throws Exception { - test(false); - test(true); + test(); } - static void test(boolean aotClassLinking) throws Exception { + static void test() throws Exception { String appJar = ClassFileInstaller.getJarPath("test.jar"); String mainClass = "LambdaWithUseImplMethodHandleApp"; String expectedMsg = "Called BaseWithProtectedMethod::protectedMethod"; @@ -63,9 +61,6 @@ static void test(boolean aotClassLinking) throws Exception { .addPrefix("-XX:ExtraSharedClassListFile=" + classList, "-cp", appJar) .setArchiveName(archiveName); - if (aotClassLinking) { - opts.addPrefix("-XX:+AOTClassLinking"); - } CDSTestUtils.createArchiveAndCheck(opts); // run with archive diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java new file mode 100644 index 000000000000..11ed8bad4cc9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.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. + * + */ + +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.constantpool.ConstantPoolBuilder; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.security.ProtectionDomain; + +public class RedefineAllAgent implements ClassFileTransformer { + public static void premain(String agentArguments, Instrumentation inst) { + inst.addTransformer(new RedefineAllAgent(), /*canRetransform=*/true); + + for (Class c : inst.getAllLoadedClasses()) { + if (!c.isArray() && !c.isHidden() && inst.isModifiableClass(c)) { + try { + // Note: we cannot use test/lib/RedefineClassHelper.java because we may + // have a regenerated class (see regeneratedClasses.cpp) such as + // java/lang/invoke/DirectMethodHandle$Holder, whose bytecodes are different than + // the DirectMethodHandle$Holder.class file stored in the JDK's modules file. + // + // Therefore, we cannot use RedefineClassHelper.getBytecodes(). We must call into + // inst.retransformClasses(), which will give us the correct bytecodes using + // JvmtiClassFileReconstituter. + inst.retransformClasses(c); + System.out.println("========= Success: " + c.getName()); + } catch (UnmodifiableClassException e) { + System.out.println("========== Failed: " + c.getName()); + e.printStackTrace(System.out); + } + } + } + } + + public byte[] transform(ClassLoader loader, String name, Class classBeingRedefined, + ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException { + try { + System.out.println((classBeingRedefined == null ? "retransforming " : "redefining ") + name); + ClassFile cf = ClassFile.of(); + ClassModel model = cf.parse(buffer); + ConstantPoolBuilder cp = ConstantPoolBuilder.of(model); + cp.utf8Entry("Hello"); + buffer = cf.build(model.thisClass(), cp, + cb -> cb.transform(model, ClassTransform.ACCEPT_ALL)); + } catch (Throwable t) { + t.printStackTrace(); + throw new RuntimeException("Unexpected", t); + } + return buffer; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf similarity index 53% rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf rename to test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf index f0b185661430..8e077b3bd3ff 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf +++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 -Premain-Class: JavaAgentTransformer -Agent-Class: JavaAgentTransformer +Premain-Class: RedefineAllAgent +Agent-Class: RedefineAllAgent Can-Retransform-Classes: true Can-Redefine-Classes: true diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java new file mode 100644 index 000000000000..e2eb7bad139b --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +/* + * @test + * @summary The agent is loaded in production run. It redefines all classes, including those already loaded from the AOT cache. + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib /test/setup_aot + * @build RedefineAllTest RedefineAllAgent JavacBenchApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * JavacBenchApp + * JavacBenchApp$ClassFile + * JavacBenchApp$FileManager + * JavacBenchApp$SourceFile + * @run driver/timeout=240 RedefineAllTest AOT + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; + +public class RedefineAllTest { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "JavacBenchApp"; + + public static String agentClasses[] = { + "RedefineAllAgent", + }; + static String agentJar; + + public static void main(String... args) throws Exception { + agentJar = ClassFileInstaller.writeJar("agent.jar", + ClassFileInstaller.Manifest.fromSourceFile("RedefineAllAgent.mf"), + agentClasses); + run(args, false); + run(args, true); + } + + static void run(String[] args, boolean compressedOops) throws Exception { + Tester t = new Tester(compressedOops); + t.run(args); + } + + static class Tester extends CDSAppTester { + boolean compressedOops; + + public Tester(boolean compressedOops) { + super(mainClass); + this.compressedOops = compressedOops; + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + String mode = "-XX:" + (compressedOops ? "+" : "-") + "UseCompressedOops"; + + if (runMode == RunMode.PRODUCTION) { + return new String[] { + mode, + "-javaagent:" + agentJar, + }; + } else { + return new String[] { + mode, + // This is needed for using the agent in production run. + "--add-modules=java.instrument", + }; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + "2", + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode.isApplicationExecuted()) { + out.shouldMatch("Generated source code for [0-9]+ classes and compiled them"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java new file mode 100644 index 000000000000..fdb105b3f2c1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +/* + * @test + * @summary The agent is loaded in production run. It redefines RedefineHotMethodApp::increment() to return 34. + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses + * @build RedefineHotMethodTest RedefineClassHelper + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar RedefineHotMethodApp RedefineClassHelper + * @run main RedefineClassHelper + * @run driver RedefineHotMethodTest AOT + */ + +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.MethodModel; + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; + +public class RedefineHotMethodTest { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = RedefineHotMethodApp.class.getName(); + + public static String agentClasses[] = { + "RedefineHotMethodAgent", + }; + static String agentJar = "redefineagent.jar"; + + public static void main(String... args) throws Exception { + Tester t = new Tester(); + t.run(args); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + if (runMode == RunMode.PRODUCTION) { + return new String[] { + "-javaagent:" + agentJar, + }; + } else { + return new String[] { + // This is needed for using the agent in production run. + "--add-modules=java.instrument", + }; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + runMode.toString(), + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.TRAINING) { + out.shouldContain("counter = 120000001"); + } else if (runMode == RunMode.PRODUCTION) { + out.shouldContain("counter = 340000001"); + } + } + } +} + +class RedefineHotMethodApp { + volatile static long counter; + + public static void main(String args[]) throws Exception { + if (args[0].equals("PRODUCTION")) { + redefineIncrementMethod(); + } + doLoop(); + counter ++; + System.out.println("counter = " + counter); + } + + static void doLoop() { + for (int i = 0; i < 1000 * 1000; i++) { + for (int j = 0; j < 10; j++) { + counter += increment(); + } + } + } + + static void redefineIncrementMethod() throws Exception { + RedefineClassHelper.redefineMethodBodies(RedefineHotMethodApp.class, + (MethodModel method) -> method.methodName().equalsString("increment"), + (CodeBuilder builder, CodeElement element) -> { + builder.loadConstant(34); + builder.ireturn(); + }); + } + + // This method will be redefined in redefineIncrementMethod() to return 34 instead. + // + // Any AOT-compiled methods that use the original version of this method + // must not be used. + static int increment() { + return 12; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.java similarity index 73% rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java rename to test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.java index 758d252f18d5..2a8bb4b0d0ea 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.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,10 +28,11 @@ * @bug 8361725 * @summary -javaagent is not allowed when creating static CDS archive * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes - * @build JavaAgent JavaAgentTransformer Util - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed - * @run driver JavaAgent STATIC + * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses + * @build SimpleTest RedefineClassHelper + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed + * @run main RedefineClassHelper + * @run driver SimpleTest STATIC */ /** @@ -39,12 +40,13 @@ * @bug 8362561 * @summary -javaagent is not allowed when creating dynamic CDS archive * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes - * @build JavaAgent JavaAgentTransformer Util - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed + * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses + * @build SimpleTest RedefineClassHelper + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. JavaAgent DYNAMIC + * @run main RedefineClassHelper + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. SimpleTest DYNAMIC */ /* @@ -52,31 +54,32 @@ * @summary -javaagent should be allowed in AOT workflow. However, classes transformed/redefined by agents will not * be cached. * @requires vm.cds.supports.aot.class.linking - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes - * @build JavaAgent JavaAgentTransformer Util - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed - * @run driver JavaAgent AOT + * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses + * @build SimpleTest RedefineClassHelper + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed + * @run main RedefineClassHelper + * @run driver SimpleTest AOT */ +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.MethodModel; + import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.helpers.ClassFileInstaller; -public class JavaAgent { +public class SimpleTest { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); - static final String mainClass = "JavaAgentApp"; + static final String mainClass = "SimpleTestApp"; public static String agentClasses[] = { - "JavaAgentTransformer", + "SimpleAgent", "Util", }; - static String agentJar; + static String agentJar = "redefineagent.jar"; public static void main(String... args) throws Exception { - agentJar = ClassFileInstaller.writeJar("agent.jar", - ClassFileInstaller.Manifest.fromSourceFile("JavaAgentTransformer.mf"), - agentClasses); - Tester t = new Tester(); if (args[0].equals("STATIC") || args[0].equals("DYNAMIC")) { // Some child processes may have non-zero exits. These are checked by @@ -123,28 +126,18 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception } } - static String agentLoadedMsg = "JavaAgentTransformer.premain() is called"; - static String agentPremainFinished = "JavaAgentTransformer::premain() is finished"; - public void checkExecutionForAOTWorkflow(OutputAnalyzer out, RunMode runMode) throws Exception { - if (runMode.isApplicationExecuted()) { - out.shouldContain(agentLoadedMsg); - out.shouldContain("Transforming: JavaAgentApp$ShouldBeTransformed; Class = null"); out.shouldContain("Result: YYYY"); // "XXXX" has been changed to "YYYY" by the agent - } else { - out.shouldNotContain(agentLoadedMsg); } switch (runMode) { case RunMode.TRAINING: - out.shouldContain(agentPremainFinished); - out.shouldContain("Skipping JavaAgentApp$ShouldBeTransformed: From ClassFileLoadHook"); - out.shouldContain("Skipping JavaAgentTransformer: Unsupported location"); + out.shouldContain("Skipping SimpleTestApp$ShouldBeTransformed: Has been redefined"); + out.shouldContain("Skipping RedefineClassHelper: Unsupported location"); break; case RunMode.ASSEMBLY: out.shouldContain("Disabled all JVMTI agents during -XX:AOTMode=create"); - out.shouldNotContain(agentPremainFinished); break; } @@ -153,7 +146,6 @@ public void checkExecutionForAOTWorkflow(OutputAnalyzer out, RunMode runMode) th public void checkExecutionForStaticWorkflow(OutputAnalyzer out, RunMode runMode) throws Exception { switch (runMode) { case RunMode.TRAINING: - out.shouldContain(agentPremainFinished); out.shouldHaveExitValue(0); break; case RunMode.DUMP_STATIC: @@ -182,14 +174,20 @@ public void checkExecutionForDynamicWorkflow(OutputAnalyzer out, RunMode runMode } } -class JavaAgentApp { - public static void main(String[] args) { +class SimpleTestApp { + public static void main(String[] args) throws Exception { + RedefineClassHelper.redefineMethodBodies(ShouldBeTransformed.class, + (MethodModel method) -> method.methodName().equalsString("toString"), + (CodeBuilder builder, CodeElement element) -> { + builder.ldc("YYYY"); + builder.areturn(); + }); System.out.println("Result: " + (new ShouldBeTransformed())); } static class ShouldBeTransformed { public String toString() { - return "XXXX"; // Will be changed to YYYY by the agent + return "XXXX"; // Will be changed to "YYYY" with class redefinition } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java index 832c9c336783..61416929e6d4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java @@ -27,6 +27,7 @@ * @summary Test the contents of -Xlog:aot+map with AOT workflow * @requires vm.cds.supports.aot.class.linking * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @modules java.base/jdk.internal.misc * @build AOTMapTest Hello * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello @@ -39,6 +40,7 @@ * @summary Test the contents of -Xlog:aot+map with dynamic CDS archive * @requires vm.cds.supports.aot.class.linking * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @modules java.base/jdk.internal.misc * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @build AOTMapTest Hello @@ -55,11 +57,16 @@ * @requires vm.cds.supports.aot.class.linking & vm.debug & vm.cds.write.archived.java.heap * @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @modules java.base/jdk.internal.value java.base/jdk.internal.misc java.base/jdk.internal.vm.annotation - * @build Hello - * @compile test-classes/AOTMapTestApp.java - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp AOTMapTestApp$Wrapper AOTMapTestApp$WrapperWrapper - * AOTMapTestApp$ArchivedData Hello - * @run main/othervm/timeout=240 AOTMapTest STATIC + * @build Hello AOTMapTest + * @compile test-classes/AOTMapTestValhallaHelper.java + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * AOTMapTestApp + * Hello + * AOTMapTestValhallaHelper + * AOTMapTestValhallaHelper$Wrapper + * AOTMapTestValhallaHelper$WrapperWrapper + * AOTMapTestValhallaHelper$ArchivedData + * @run main/othervm/timeout=240 AOTMapTest AOT --two-step-training Valhalla */ import java.io.File; @@ -74,12 +81,9 @@ public class AOTMapTest { static final String appJar = ClassFileInstaller.getJarPath("app.jar"); static final String mainClass = "AOTMapTestApp"; static final String classLoadLogFile = "production.class.load.log"; - + static boolean testValhalla; public static void main(String[] args) throws Exception { - doTest(args); - } - - public static void doTest(String[] args) throws Exception { + testValhalla = args.length >= 3 && args[2].equals("Valhalla"); Tester tester = new Tester(); tester.run(args); @@ -126,15 +130,17 @@ public String[] vmArgs(RunMode runMode) { vmArgs.add("-Xmx128M"); vmArgs.add("-Xlog:aot=debug"); + vmArgs.add("--add-exports"); + vmArgs.add("java.base/jdk.internal.misc=ALL-UNNAMED"); - if (isStaticWorkflow()) { + if (testValhalla) { vmArgs.add("--enable-preview"); vmArgs.add("--add-exports"); vmArgs.add("java.base/jdk.internal.value=ALL-UNNAMED"); - vmArgs.add("--add-exports"); - vmArgs.add("java.base/jdk.internal.misc=ALL-UNNAMED"); - vmArgs.add("-Xbootclasspath/a:" + appJar); - vmArgs.add("-XX:ArchiveHeapTestClass=AOTMapTestApp"); + + if (runMode == RunMode.ASSEMBLY) { + vmArgs.add("-XX:AOTInitTestClass=AOTMapTestValhallaHelper"); + } } // filesize=0 ensures that a large map file not broken up in multiple files. @@ -155,6 +161,7 @@ public String[] vmArgs(RunMode runMode) { public String[] appCommandLine(RunMode runMode) { return new String[] { mainClass, + testValhalla ? "Valhalla" : "none" }; } } @@ -165,6 +172,12 @@ class AOTMapTestApp { public static void main(String[] args) throws Exception { System.out.println("Hello AOTMapTestApp"); testCustomLoader(); + + if (args[0].equals("Valhalla")) { + Class c = Class.forName("AOTMapTestValhallaHelper"); + Object o = c.newInstance(); + System.out.println(o); + } } static void testCustomLoader() throws Exception { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java new file mode 100644 index 000000000000..94f2234f2a40 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.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 Test AOT-cached flat arrays + * @requires vm.cds.supports.aot.class.linking + * @requires vm.debug + * @enablePreview + * @library /test/jdk/lib/testlibrary /test/lib + * @modules java.base/jdk.internal.value + * @build FlatArrayTest + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * FlatArrayTestApp MyAOTInitedClass CharPair Wrapper + * @run driver FlatArrayTest AOT --two-step-training + */ + +import java.util.Arrays; +import jdk.internal.value.ValueClass; + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.StringArrayUtils; + +public class FlatArrayTest { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = FlatArrayTestApp.class.getName(); + + public static void main(String[] args) throws Exception { + new Tester().run(args); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + String args[] = StringArrayUtils.concat("--enable-preview", + "--add-exports", + "java.base/jdk.internal.value=ALL-UNNAMED"); + if (runMode == RunMode.ASSEMBLY) { + args = StringArrayUtils.concat(args, + "-Xlog:aot+class=debug", + "-XX:AOTInitTestClass=MyAOTInitedClass"); + } + + return args; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + runMode.toString(), + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.TRAINING) { + out.shouldContain("Y = 123"); + } else if (runMode == RunMode.ASSEMBLY) { + out.shouldMatch("klasses.* app .*MyAOTInitedClass .* inited"); + out.shouldMatch("klasses.* app .*CharPair .* inited"); + out.shouldMatch("klasses.* app .*Wrapper .* inited"); + } else if (runMode == RunMode.PRODUCTION) { + out.shouldContain("Y = 45"); + } + } + } +} + +class FlatArrayTestApp { + static int X = 45; + public static void main(String[] args) { + X = 123; + MyAOTInitedClass.test(args[0]); + } +} + +value class CharPair implements Comparable { + char c0, c1; + + public String toString() { + return "(" + c0 + ", " + c1 + ")"; + } + + public int compareTo(CharPair o) { + return (c0 - o.c0) - (c1 - o.c1); + } + + public CharPair(char c0, char c1) { + this.c0 = c0; + this.c1 = c1; + } +} + +value class Wrapper implements Comparable { + Integer i; + + public String toString() { + return i.toString(); + } + + public int compareTo(Wrapper o) { + return i - o.i; + } + + Wrapper(int i) { + this.i = new Integer(i); + } +} + +// This class is stored in the AOT cache in the initialized state. +class MyAOTInitedClass { + // Note that when MyAOTInitedClass is initialized in the assembly run, FlatArrayTestApp.main() + // is not executed, so the cached value of MyAOTInitedClass.Y will be 45; + static int Y = FlatArrayTestApp.X; + + static Integer[] intArray; + static CharPair[] charPairArray; + static Wrapper[] wrapperArray; + + static CharPair charPair; + static Wrapper wrapper; + + static { + intArray = new Integer[3]; + intArray[0] = null; + System.out.println("TEST: " + (intArray[0] == null)); + intArray[0] = new Integer(0); + intArray[1] = new Integer(1); + intArray[2] = new Integer(2); + + charPairArray = new CharPair[3]; + charPairArray[0] = new CharPair('a', 'b'); + charPairArray[1] = new CharPair('c', 'd'); + charPairArray[2] = new CharPair('e', 'f'); + + wrapperArray = new Wrapper[3]; + wrapperArray[0] = new Wrapper(0); + wrapperArray[1] = new Wrapper(1); + wrapperArray[2] = new Wrapper(2); + + charPair = new CharPair('x', 'y'); + wrapper = new Wrapper(5); + } + + static void test(String runMode) { + System.out.println("Y = " + Y); + if (runMode.equals("PRODUCTION") && Y != 45) { + throw new RuntimeException("MyAOTInitedClass must be AOT-inited"); + } + + if (!ValueClass.isFlatArray(intArray)) { + throw new RuntimeException("Integer array should be flat"); + } + + if (!ValueClass.isFlatArray(charPairArray)) { + throw new RuntimeException("CharPair array should be flat"); + } + + if (!ValueClass.isFlatArray(wrapperArray)) { + throw new RuntimeException("Wrapper array should be flat"); + } + + // Ensure archived arrays are restored properly + Integer[] runtimeIntArray = new Integer[3]; + runtimeIntArray[0] = new Integer(0); + runtimeIntArray[1] = new Integer(1); + runtimeIntArray[2] = new Integer(2); + + CharPair[] runtimeCharPairArray = new CharPair[3]; + runtimeCharPairArray[0] = new CharPair('a', 'b'); + runtimeCharPairArray[1] = new CharPair('c', 'd'); + runtimeCharPairArray[2] = new CharPair('e', 'f'); + + Wrapper[] runtimeWrapperArray = new Wrapper[3]; + runtimeWrapperArray[0] = new Wrapper(0); + runtimeWrapperArray[1] = new Wrapper(1); + runtimeWrapperArray[2] = new Wrapper(2); + + if (Arrays.compare(intArray, runtimeIntArray) != 0) { + throw new RuntimeException("Integer array not restored correctly"); + } + + if (Arrays.compare(charPairArray, runtimeCharPairArray) != 0) { + throw new RuntimeException("CharPair array not restored correctly"); + } + + if (Arrays.compare(wrapperArray, runtimeWrapperArray) != 0) { + throw new RuntimeException("Wrapper array not restored correctly"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java new file mode 100644 index 000000000000..9e7e77a40c65 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @summary Disallow different module options between AOT training and assembly + * @bug 8388525 + * @requires vm.cds + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build Hello + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello + * @run driver InvalidModuleOptions + */ + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.cds.CDSModulePackager; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.StringArrayUtils; + +public class InvalidModuleOptions { + static final Path modulesSrc = Paths.get(System.getProperty("test.src")).resolve("modules"); + static final Path modulesSrc2 = Paths.get(System.getProperty("test.src")).resolve("modules2"); + static final Path modulesSrc3 = Paths.get(System.getProperty("test.src")).resolve("modules3"); + static String appJar = ClassFileInstaller.getJarPath("hello.jar"); + static String aotConfigFile = "hello.aotconfig"; + static String aotCacheFile = "hello.aot"; + static String helloClass = "Hello"; + static String modulePath1; + static String modulePath2; + static String modulePath3; + + static String[][] testCases = { + // --module-path + { + "", null, + "module path has fewer elements (0) than expected (1)", + }, + { + "", "", + "module path at [2] is different", + }, + { + "", "", + "module path at [3] is different", + }, + { + null, "", + "module path has more elements (1) than expected (0)", + }, + + // --add-modules + { + null, "--add-modules=java.instrument", + "Mismatched values for property jdk.module.addmods: java.instrument specified for current JVM but not in AOTConfiguration" + }, + { + "--add-modules=java.instrument", null, + "Mismatched values for property jdk.module.addmods: java.instrument specified in AOTConfiguration but not for current JVM"}, + { + "--add-modules=java.instrument", "--add-modules=java.base", + "Mismatched values for property jdk.module.addmods: current = java.base, AOTConfiguration = java.instrument" + }, + + // --enable-native-access + // Just test one variation, as HotSpot handles this the same way as --add-modules. + { + null, "--enable-native-access=ALL_UNNAMED", + "Mismatched values for property jdk.module.enable.native.access: ALL_UNNAMED specified for current JVM but not in AOTConfiguration" + }, + + // -Djdk.module.showModuleResolution=true also disabled full module graph: + { + null, "-Djdk.module.showModuleResolution=true", + "full module graph: disabled due to incompatible property: jdk.module.showModuleResolution=true", + "AOT class linking was enabled in training run but has been disabled due to incompatible module options", + }, + }; + + public static void main(String[] args) throws Exception { + CDSModulePackager modulePackager1 = new CDSModulePackager(modulesSrc, Paths.get("test-modules1")); + modulePackager1.createModularJar("com.test"); + modulePath1 = modulePackager1.getOutputDir().toString(); + + CDSModulePackager modulePackager2 = new CDSModulePackager(modulesSrc2, Paths.get("test-modules2")); + modulePackager2.createModularJar("com.moretest"); + modulePath2 = modulePackager2.getOutputDir().toString(); + + CDSModulePackager modulePackager3 = new CDSModulePackager(modulesSrc3, Paths.get("test-modules3")); + modulePackager3.createModularJar("com.evenmoretest"); + modulePath3 = modulePackager3.getOutputDir().toString(); + + for (int i = 0; i < testCases.length; i++) { + String[] testSpec = testCases[i]; + String trainOpt = testSpec[0]; + String assemblyOpt = testSpec[1]; + + String[] trainCmds = new String[] { + "-XX:AOTMode=record", + "-XX:AOTConfiguration=" + aotConfigFile, + "-Xlog:aot=debug", + "-cp", appJar + }; + if (trainOpt != null) { + trainCmds = concat(trainCmds, trainOpt); + } + trainCmds = StringArrayUtils.concat(trainCmds, helloClass); + + String[] assemblyCmds = new String[] { + "-XX:AOTMode=create", + "-XX:AOTConfiguration=" + aotConfigFile, + "-XX:AOTCache=" + aotCacheFile, + "-Xlog:cds,aot,class+path", + "-cp", appJar + }; + if (assemblyOpt != null) { + assemblyCmds = concat(assemblyCmds, assemblyOpt); + } + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(trainCmds); + OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train" + i); + out.shouldContain("Hello World"); + out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); + out.shouldHaveExitValue(0); + + pb = ProcessTools.createLimitedTestJavaProcessBuilder(assemblyCmds); + out = CDSTestUtils.executeAndLog(pb, "asm" + i); + out.shouldContain("An error has occurred while writing the AOT cache"); + for (int j = 2; j < testSpec.length; j++) { + out.shouldContain(testSpec[j]); + } + out.shouldNotContain("AOTCache creation is complete"); + out.shouldNotHaveExitValue(0); + } + } + + static String[] concat(String[] opts, String extra) { + if (extra.equals("")) { + return StringArrayUtils.concat(opts, "--module-path", modulePath1); + } else if (extra.equals("")) { + return StringArrayUtils.concat(opts, "--module-path", modulePath2); + } else if (extra.equals("")) { + return StringArrayUtils.concat(opts, "--module-path", modulePath1 + File.pathSeparator + modulePath2); + } else if (extra.equals("")) { + return StringArrayUtils.concat(opts, "--module-path", modulePath1 + File.pathSeparator + modulePath3); + } else { + return StringArrayUtils.concat(opts, extra); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java deleted file mode 100644 index 123e4a0d72b0..000000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java +++ /dev/null @@ -1,71 +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. - * - */ - -import java.lang.System.Logger.Level; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.security.ProtectionDomain; - -// This class is available on the classpath so it can be accessed by JavaAgentApp -public class JavaAgentTransformer implements ClassFileTransformer { - private static Instrumentation savedInstrumentation; - private static final System.Logger LOGGER = System.getLogger(JavaAgentTransformer.class.getName()); - - public static void premain(String agentArguments, Instrumentation instrumentation) { - System.out.println("JavaAgentTransformer.premain() is called"); - instrumentation.addTransformer(new JavaAgentTransformer(), /*canRetransform=*/true); - savedInstrumentation = instrumentation; - - LOGGER.log(Level.WARNING, "JavaAgentTransformer::premain() is finished"); - } - - public static Instrumentation getInstrumentation() { - return savedInstrumentation; - } - - public static void agentmain(String args, Instrumentation inst) throws Exception { - premain(args, inst); - } - - public byte[] transform(ClassLoader loader, String name, Class classBeingRedefined, - ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException { - if (name.equals("JavaAgentApp$ShouldBeTransformed")) { - System.out.println("Transforming: " + name + "; Class = " + classBeingRedefined); - try { - replace(buffer, "XXXX", "YYYY"); - } catch (Throwable t) { - t.printStackTrace(); - } - Thread.dumpStack(); - return buffer; - } - return null; - } - - static void replace(byte[] buffer, String from, String to) { - int n = Util.replace(buffer, from, to); - System.out.println("..... replaced " + n + " occurrence(s) of '" + from + "' to '" + to + "'"); - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java index b0db450e3525..55eab06c33f1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java @@ -48,11 +48,6 @@ public static void main(String[] args) throws Exception { // Explicitly disable Tester tester1 = new Tester("-XX:-AOTClassLinking"); tester1.run(new String[] {"AOT", "--two-step-training"} ); - - // Full module graph caching is disabled with -Djdk.module.showModuleResolution=true. - // This will disable AOT class linking. - Tester tester2 = new Tester("-Djdk.module.showModuleResolution=true"); - tester2.run(new String[] {"AOT", "--two-step-training"} ); } static class Tester extends CDSAppTester { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVerification.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVerification.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTLoaderConstraintsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTLoaderConstraintsTest.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTLoaderConstraintsTest.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTLoaderConstraintsTest.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddExports.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddExports.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java similarity index 94% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java index 95fc9f0f7000..347e61fed4ca 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java @@ -162,7 +162,11 @@ public String modulepath(RunMode runMode) { public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (runMode == RunMode.PRODUCTION) { out.shouldContain("full module graph: disabled"); - out.shouldContain("Mismatched values for property jdk.module.addreads: runtime com.norequires=ALL-UNNAMED dump time com.norequires=org.astro"); + if (isStaticWorkflow()) { + out.shouldContain("Mismatched values for property jdk.module.addreads: runtime com.norequires=ALL-UNNAMED dump time com.norequires=org.astro"); + } else { + out.shouldContain("Mismatched values for property jdk.module.addreads: current = com.norequires=ALL-UNNAMED, AOTCache = com.norequires=org.astro"); + } } else if (runMode == RunMode.ASSEMBLY) { out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); } else { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass2.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass2.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass3.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass3.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass4.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass4.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass2.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass2.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass3.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass3.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass4.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass4.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassA.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassA.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassA.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassA.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassB.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassB.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassB.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassB.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BootClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BootClass.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BootClass.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BootClass.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java similarity index 94% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java index 482f8fb4ad00..9bebaa7a1522 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java @@ -91,15 +91,18 @@ public static void main(String[] args) throws Exception { // Run without archived FMG -- fail to load { - final String archiveType = (args[0].equals("AOT")) ? "AOT cache" : "shared archive file"; String extraVmArgs[] = { - "-Xlog:cds", + "-Xlog:aot,cds", "-Djdk.module.showModuleResolution=true" }; t.setCheckExitValue(false); OutputAnalyzer out = t.productionRun(extraVmArgs); - out.shouldHaveExitValue(1); - out.shouldContain(archiveType + " has aot-linked classes. It cannot be used when archived full module graph is not used."); + if (args[0].equals("AOT")) { + out.shouldHaveExitValue(1); + out.shouldContain("AOT cache has aot-linked classes. It cannot be used when archived full module graph is not used."); + } else { + out.shouldHaveExitValue(0); + } t.setCheckExitValue(true); } } @@ -117,10 +120,14 @@ public String classpath(RunMode runMode) { @Override public String[] vmArgs(RunMode runMode) { - return new String[] { - "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug", - "-XX:+AOTClassLinking", - }; + if (runMode == RunMode.DUMP_STATIC) { + return new String[] { "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug" }; + } else { + return new String[] { + "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug", + "-XX:+AOTClassLinking", + }; + } } @Override diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/FakeCodeLocation.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/FakeCodeLocation.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/FakeCodeLocation.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/FakeCodeLocation.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GeneratedInternedString.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GeneratedInternedString.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GeneratedInternedString.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GeneratedInternedString.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GoodOldClass.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GoodOldClass.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/InitiatingLoaderTester.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/InitiatingLoaderTester.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/InitiatingLoaderTester.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/InitiatingLoaderTester.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java similarity index 90% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java index c91e999c40fe..45bcad3f9b9e 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java @@ -30,11 +30,10 @@ * @library /test/jdk/lib/testlibrary /test/lib * @build LambdaInExcludedClass * @run driver jdk.test.lib.helpers.ClassFileInstaller LambdaInExcludedClassApp - * @run driver LambdaInExcludedClass STATIC + * @run driver LambdaInExcludedClass AOT */ import jdk.test.lib.cds.CDSAppTester; -import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.lib.process.OutputAnalyzer; public class LambdaInExcludedClass { @@ -42,7 +41,7 @@ public class LambdaInExcludedClass { public static void main(String[] args) throws Exception { Tester t = new Tester(); - t.run(args); + t.runAOTWorkflow(args); } static class Tester extends CDSAppTester { @@ -60,8 +59,8 @@ public String classpath(RunMode runMode) { @Override public String[] vmArgs(RunMode runMode) { return new String[] { + "-Xlog:aot", "-Xmx128m", - "-XX:+AOTClassLinking", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseEpsilonGC", }; @@ -76,10 +75,9 @@ public String[] appCommandLine(RunMode runMode) { @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { - if (runMode == RunMode.DUMP_STATIC) { + if (runMode == RunMode.TRAINING) { out.shouldContain("Skipping LambdaInExcludedClassApp: Unsupported location"); - out.shouldContain("Cannot aot-resolve constants for LambdaInExcludedClassApp because it is excluded"); - } else { + } else if (runMode == RunMode.PRODUCTION) { out.shouldContain("Hello LambdaInExcludedClassApp"); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/MethodHandleTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/MethodHandleTest.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/MethodHandleTest.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/MethodHandleTest.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java similarity index 99% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java index 81bf28010b1c..676a98ff387a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java @@ -64,9 +64,7 @@ public String classpath(RunMode runMode) { } public String[] vmArgs(RunMode runMode) { - return new String[] { - "-XX:+AOTClassLinking", // by default enables AOTInvokeDynamicLinking - }; + return new String[0]; } @Override diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TestSetupAOTTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TestSetupAOTTest.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TrainingRun.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TrainingRun.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/WeakReferenceTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/WeakReferenceTest.java similarity index 100% rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/WeakReferenceTest.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/WeakReferenceTest.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java new file mode 100644 index 000000000000..6d95229f96f8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.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 com.moretest; + +public class Bar {} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java similarity index 66% rename from test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java index c8ab0db7d34d..7fd2c23a27ac 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright Amazon.com Inc. 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,17 +22,6 @@ * */ -import jdk.internal.misc.CDS; - -public class ArchivedIntegerHolder { - public static Object[] archivedObjects; - static { - CDS.initializeFromArchive(ArchivedIntegerHolder.class); - if (archivedObjects == null) { - archivedObjects = new Object[256]; - for (int i = -128; i <= 127; i++) { - archivedObjects[i + 128] = Integer.valueOf(i); - } - } - } +module com.moretest { + exports com.moretest; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java new file mode 100644 index 000000000000..9f077be2ce4a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.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 com.evenmoretest; + +public class Baz {} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java new file mode 100644 index 000000000000..4aaf8811fe4e --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +module com.evenmoretest { + exports com.evenmoretest; +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java similarity index 71% rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java index c8b338cbc42d..e237e5de7be7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java @@ -21,13 +21,13 @@ * questions. */ -import jdk.internal.misc.PreviewFeatures; import jdk.internal.value.ValueClass; import jdk.internal.vm.annotation.NullRestricted; // Test app for flat arrays in the AOT map -public class AOTMapTestApp { - +// This class is AOT-initialized during the AOT assembly phase (see +// the use of -XX:AOTInitTestClass in ../AOTMapTest.java) +public class AOTMapTestValhallaHelper { public static value class Wrapper implements Comparable { Integer i; @@ -96,48 +96,42 @@ public static class ArchivedData { wrapperWrapper = new WrapperWrapper(0xbbbb6666); a = 0x7788; b = new Integer(0x8899); + super(); } } - static ArchivedData archivedObjects; - static { - if (archivedObjects == null) { - archivedObjects = new ArchivedData(); - } else { - System.out.println("Initialized from CDS"); - System.out.println("boxArray " + archivedObjects.boxArray); - System.out.println("wrapperArray " + archivedObjects.wrapperArray); - System.out.println("wrapperWrapperArray " + archivedObjects.wrapperWrapperArray); - System.out.println("objArray " + archivedObjects.objArray); - System.out.println("wrapper " + archivedObjects.wrapper); - System.out.println("wrapperWrapper " + archivedObjects.wrapperWrapper); - System.out.println("a " + archivedObjects.a); - System.out.println("b " + archivedObjects.b); - } - } - - public static void main(String[] args) throws Exception { - System.out.println("Hello FlatAOTMapTestApp"); - Class.forName("Hello"); - - if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.boxArray)) { + // This object will be stored in the AOT cache. + static ArchivedData archivedObjects = new ArchivedData(); + + // This is called by reflection code in AOTMapTestApp.main() in ../AOTMapTest.java + public AOTMapTestValhallaHelper() { + System.out.println("boxArray " + archivedObjects.boxArray); + System.out.println("wrapperArray " + archivedObjects.wrapperArray); + System.out.println("wrapperWrapperArray " + archivedObjects.wrapperWrapperArray); + System.out.println("objArray " + archivedObjects.objArray); + System.out.println("wrapper " + archivedObjects.wrapper); + System.out.println("wrapperWrapper " + archivedObjects.wrapperWrapper); + System.out.println("a " + archivedObjects.a); + System.out.println("b " + archivedObjects.b); + + if (!ValueClass.isFlatArray(archivedObjects.boxArray)) { throw new RuntimeException("Boxing class array should be flat"); } - if (PreviewFeatures.isEnabled() && (!ValueClass.isNullRestrictedArray(archivedObjects.nullFreeBoxArray) || + if ((!ValueClass.isNullRestrictedArray(archivedObjects.nullFreeBoxArray) || !ValueClass.isFlatArray(archivedObjects.nullFreeBoxArray))) { throw new RuntimeException("Boxing class array should be null-free and flat"); } - if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.wrapperArray)) { + if (!ValueClass.isFlatArray(archivedObjects.wrapperArray)) { throw new RuntimeException("Wrapper array should be flat"); } - if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.wrapperWrapperArray)) { + if (!ValueClass.isFlatArray(archivedObjects.wrapperWrapperArray)) { throw new RuntimeException("WrapperWrapper array should be flat"); } - if (PreviewFeatures.isEnabled() && ValueClass.isFlatArray(archivedObjects.objArray)) { + if (ValueClass.isFlatArray(archivedObjects.objArray)) { throw new RuntimeException("Object array should not be flat"); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java deleted file mode 100644 index 8014ed685ee1..000000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java index eebace23feaf..990a5e81e127 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java @@ -27,7 +27,7 @@ * @summary CPU feature compatibility test for AOT Code Cache * @requires vm.cds.supports.aot.code.caching * @requires vm.compMode != "Xcomp" & vm.compMode != "Xint" - * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" + * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64" * @comment The test verifies AOT checks during VM startup and not code generation. * No need to run it with -Xcomp. * @library /test/lib /test/setup_aot @@ -74,6 +74,11 @@ public static void main(String... args) throws Exception { testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.MISSING); testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.ADDITIONAL); } + } else if (Platform.isRISCV64()) { + if (isZbaSupported(cpuFeatures)) { + testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.MISSING); + testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.ADDITIONAL); + } } } @@ -87,12 +92,14 @@ public String[] vmArgs(RunMode runMode) { if (mode == IncompatibilityMode.MISSING) { return new String[] {"-Xlog:aot+codecache*=debug"}; } else { - return new String[] {vmOption, "-Xlog:aot+codecache*=debug"}; + // UnlockDiagnosticVMOptions must precede vmOption because + // some tested CPU feature flags are diagnostic (e.g. UseZba on riscv). + return new String[] {"-XX:+UnlockDiagnosticVMOptions", vmOption, "-Xlog:aot+codecache*=debug"}; } } else if (runMode == RunMode.PRODUCTION) { if (mode == IncompatibilityMode.MISSING) { - return new String[] {vmOption, - "-XX:+UnlockDiagnosticVMOptions", + return new String[] {"-XX:+UnlockDiagnosticVMOptions", + vmOption, // Prevent exiting VM on failure "-XX:-AbortVMOnAOTCodeFailure", "-Xlog:aot+codecache*=debug"}; @@ -149,4 +156,9 @@ static boolean isAVXSupported(List cpuFeatures) { static boolean isCRC32Supported(List cpuFeatures) { return cpuFeatures.contains("crc32"); } + + // Only used on riscv64 platform + static boolean isZbaSupported(List cpuFeatures) { + return cpuFeatures.contains("zba"); + } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java index f43155bf6d57..718793902390 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java @@ -476,8 +476,20 @@ static void negativeTests() throws Exception { out.shouldHaveExitValue(1); //---------------------------------------------------------------------- - printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache"); + printTestCase("Cannot use a classic CDS archive with -XX:+AOTClassLinking"); String staticArchive = "static.jsa"; + + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xshare:dump", + "-XX:SharedArchiveFile=" + staticArchive, + "-XX:+AOTClassLinking"); + out = CDSTestUtils.executeAndLog(pb, "static"); + out.shouldContain("AOTClassLinking is not supported for classic CDS archive"); + out.shouldHaveExitValue(0); + + //---------------------------------------------------------------------- + printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache"); + staticArchive = "static.jsa"; String dynamicArchive = "dynamic.jsa"; pb = ProcessTools.createLimitedTestJavaProcessBuilder( diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java deleted file mode 100644 index 3b1ccff1bfa7..000000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @bug 8214781 8293187 - * @summary Test for the -XX:ArchiveHeapTestClass flag - * @requires vm.debug == true & vm.cds.write.archived.java.heap - * @requires vm.cds.supports.aot.class.linking - * @modules java.logging - * @library /test/jdk/lib/testlibrary /test/lib - * /test/hotspot/jtreg/runtime/cds/appcds - * /test/hotspot/jtreg/runtime/cds/appcds/test-classes - * @build ArchiveHeapTestClass Hello pkg.ClassInPackage - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar - * CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY - * CDSTestClassB CDSTestClassC CDSTestClassD - * CDSTestClassE CDSTestClassF - * pkg.ClassInPackage - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello - * @run driver ArchiveHeapTestClass - */ - -import jdk.test.lib.cds.CDSTestUtils; -import jdk.test.lib.Platform; -import jdk.test.lib.helpers.ClassFileInstaller; -import jdk.test.lib.process.OutputAnalyzer; - -public class ArchiveHeapTestClass { - static final String bootJar = ClassFileInstaller.getJarPath("boot.jar"); - static final String appJar = ClassFileInstaller.getJarPath("app.jar"); - static final String[] appClassList = {"Hello"}; - - static final String CDSTestClassA_name = CDSTestClassA.class.getName(); - static final String CDSTestClassB_name = CDSTestClassB.class.getName(); - static final String CDSTestClassC_name = CDSTestClassC.class.getName(); - static final String CDSTestClassD_name = CDSTestClassD.class.getName(); - static final String CDSTestClassE_name = CDSTestClassE.class.getName(); - static final String CDSTestClassF_name = CDSTestClassF.class.getName(); - static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/'); - static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects"; - - public static void main(String[] args) throws Exception { - testDebugBuild(); - } - - static OutputAnalyzer dumpHelloOnly(String... extraOpts) throws Exception { - return TestCommon.dump(appJar, appClassList, extraOpts); - } - - static OutputAnalyzer dumpBootAndHello(String bootClass, String... extraOpts) throws Exception { - String classlist[] = TestCommon.concat(appClassList, bootClass); - extraOpts = TestCommon.concat(extraOpts, - "-Xbootclasspath/a:" + bootJar, - "-XX:ArchiveHeapTestClass=" + bootClass, - "-Xlog:aot+heap"); - return TestCommon.dump(appJar, classlist, extraOpts); - } - - static int caseNum = 0; - static void testCase(String s) { - System.out.println("=================================================="); - System.out.println(" Test " + (++caseNum) + ": " + s); - } - - static void mustContain(OutputAnalyzer output, String... expectStrs) throws Exception { - for (String s : expectStrs) { - output.shouldContain(s); - } - } - - static void mustFail(OutputAnalyzer output, String... expectStrs) throws Exception { - mustContain(output, expectStrs); - output.shouldNotHaveExitValue(0); - } - - static void mustSucceed(OutputAnalyzer output, String... expectStrs) throws Exception { - mustContain(output, expectStrs); - output.shouldHaveExitValue(0); - } - - static void testDebugBuild() throws Exception { - OutputAnalyzer output; - - testCase("Simple positive case"); - output = dumpBootAndHello(CDSTestClassA_name); - mustSucceed(output, CDSTestClassA.getOutput()); // make sure is executed - output.shouldMatch("warning.*aot.*Loading ArchiveHeapTestClass " + CDSTestClassA_name); - output.shouldMatch("warning.*aot.*Initializing ArchiveHeapTestClass " + CDSTestClassA_name); - output.shouldContain("Archived field " + CDSTestClassA_name + "::" + ARCHIVE_TEST_FIELD_NAME); - output.shouldMatch("Archived object klass CDSTestClassA .*\\[LCDSTestClassA;"); - output.shouldMatch("Archived object klass CDSTestClassA .*CDSTestClassA\\$YY"); - - TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:aot+heap", CDSTestClassA_name) - .assertNormalExit(CDSTestClassA.getOutput(), - "resolve subgraph " + CDSTestClassA_name); - - testCase("Class doesn't exist"); - output = dumpHelloOnly("-XX:ArchiveHeapTestClass=NoSuchClass"); - mustFail(output, "Fail to initialize archive heap: NoSuchClass cannot be loaded"); - - testCase("Class doesn't exist (objarray)"); - output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[LNoSuchClass;"); - mustFail(output, "Fail to initialize archive heap: [LNoSuchClass; cannot be loaded"); - - testCase("Not an instance klass"); - output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[Ljava/lang/Object;"); - mustFail(output, "Fail to initialize archive heap: [Ljava/lang/Object; is not an instance class"); - - testCase("Not in boot loader"); - output = dumpHelloOnly("-XX:ArchiveHeapTestClass=Hello"); - mustFail(output, "Fail to initialize archive heap: Hello cannot be loaded by the boot loader"); - - testCase("Not from unnamed module"); - output = dumpHelloOnly("-XX:ArchiveHeapTestClass=java/lang/Object"); - mustFail(output, "ArchiveHeapTestClass java/lang/Object is not in unnamed module"); - - testCase("Not from unnamed package"); - output = dumpBootAndHello(ClassInPackage_name); - mustFail(output, "ArchiveHeapTestClass pkg/ClassInPackage is not in unnamed package"); - - testCase("Field not found"); - output = dumpBootAndHello(CDSTestClassB_name); - mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassB::archivedObjects"); - - testCase("Not a static field"); - output = dumpBootAndHello(CDSTestClassC_name); - mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassC::archivedObjects"); - - testCase("Not a T_OBJECT field"); - output = dumpBootAndHello(CDSTestClassD_name); - mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects"); - - if (!CDSTestUtils.isAOTClassLinkingEnabled()) { - testCase("Use a disallowed class: in unnamed module but not in unname package"); - output = dumpBootAndHello(CDSTestClassE_name); - mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap"); - - testCase("Use a disallowed class: not in java.base module"); - output = dumpBootAndHello(CDSTestClassF_name); - mustFail(output, "Class java.util.logging.Level not allowed in archive heap"); - } - } -} - -class CDSTestClassA { - static final String output = "CDSTestClassA. was executed"; - static Object[] archivedObjects; - static { - // The usual convention would be to call this here: - // CDS.initializeFromArchive(CDSTestClassA.class); - // However, the CDS class is not exported to the unnamed module by default, - // and we don't want to use "--add-exports java.base/jdk.internal.misc=ALL-UNNAMED", as - // that would disable the archived full module graph, which will disable - // CDSConfig::is_using_aot_linked_classes(). - // - // Instead, HeapShared::initialize_test_class_from_archive() will set up the - // "archivedObjects" field first, before calling CDSTestClassA.. So - // if we see that archivedObjects is magically non-null here, that means - // it has been restored from the CDS archive. - if (archivedObjects == null) { - archivedObjects = new Object[5]; - archivedObjects[0] = output; - archivedObjects[1] = new CDSTestClassA[0]; - archivedObjects[2] = new YY(); - archivedObjects[3] = new int[0]; - archivedObjects[4] = new int[2][2]; - } else { - System.out.println("Initialized from CDS"); - } - System.out.println(output); - System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule()); - System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage()); - System.out.println("CDSTestClassA[] module = " + archivedObjects[1].getClass().getModule()); - System.out.println("CDSTestClassA[] package = " + archivedObjects[1].getClass().getPackage()); - } - - static String getOutput() { - return output; - } - - public static void main(String args[]) { - if (CDSTestClassA.class.getModule().isNamed()) { - throw new RuntimeException("CDSTestClassA must be in unnamed module"); - } - if (CDSTestClassA.class.getPackage() != null) { - throw new RuntimeException("CDSTestClassA must be in null package"); - } - if (archivedObjects[1].getClass().getModule().isNamed()) { - throw new RuntimeException("CDSTestClassA[] must be in unnamed module"); - } - if (archivedObjects[1].getClass().getPackage() != null) { - throw new RuntimeException("CDSTestClassA[] must be in null package"); - } - XX.doit(); - YY.doit(); - } - - // This is an inner class that has NOT been archived. - static class XX { - static void doit() { - System.out.println("XX module = " + XX.class.getModule()); - System.out.println("XX package = " + XX.class.getPackage()); - - if (XX.class.getModule().isNamed()) { - throw new RuntimeException("XX must be in unnamed module"); - } - if (XX.class.getPackage() != null) { - throw new RuntimeException("XX must be in null package"); - } - } - } - - // This is an inner class that HAS been archived. - static class YY { - static void doit() { - System.out.println("YY module = " + YY.class.getModule()); - System.out.println("YY package = " + YY.class.getPackage()); - - if (YY.class.getModule().isNamed()) { - throw new RuntimeException("YY must be in unnamed module"); - } - if (YY.class.getPackage() != null) { - throw new RuntimeException("YY must be in null package"); - } - } - } -} - -class CDSTestClassB { - // No field named "archivedObjects" -} - -class CDSTestClassC { - Object[] archivedObjects; // Not a static field -} - -class CDSTestClassD { - static int archivedObjects; // Not an int field -} - -class CDSTestClassE { - static Object[] archivedObjects; - static { - // Not in unnamed package of unnamed module - archivedObjects = new Object[1]; - archivedObjects[0] = new pkg.ClassInPackage(); - } -} - -class CDSTestClassF { - static Object[] archivedObjects; - static { - // Not in java.base - archivedObjects = new Object[1]; - archivedObjects[0] = java.util.logging.Level.OFF; - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java deleted file mode 100644 index ec6944cda459..000000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -import java.util.Arrays; -import jdk.internal.value.ValueClass; - -public class ArchivedFlatArrayApp { - - // Check that arrays of both migrated value - // classes and custom value classes are archived - public static class ArchivedData { - Integer[] intArray; - CharPair[] charPairArray; - Wrapper[] wrapperArray; - } - - public static value class CharPair implements Comparable { - char c0, c1; - - public String toString() { - return "(" + c0 + ", " + c1 + ")"; - } - - public int compareTo(CharPair o) { - return (c0 - o.c0) - (c1 - o.c1); - } - - public CharPair(char c0, char c1) { - this.c0 = c0; - this.c1 = c1; - } - } - - public static value class Wrapper implements Comparable { - Integer i; - - public String toString() { - return i.toString(); - } - - public int compareTo(Wrapper o) { - return i - o.i; - } - - Wrapper(int i) { - this.i = new Integer(i); - } - } - - static ArchivedData archivedObjects; - static boolean restored; - static { - if (archivedObjects == null) { - restored = false; - System.out.println("Not archived"); - archivedObjects = new ArchivedData(); - - archivedObjects.intArray = new Integer[3]; - archivedObjects.intArray[0] = new Integer(0); - archivedObjects.intArray[1] = new Integer(1); - archivedObjects.intArray[2] = new Integer(2); - - archivedObjects.charPairArray = new CharPair[3]; - archivedObjects.charPairArray[0] = new CharPair('a', 'b'); - archivedObjects.charPairArray[1] = new CharPair('c', 'd'); - archivedObjects.charPairArray[2] = new CharPair('e', 'f'); - - archivedObjects.wrapperArray = new Wrapper[3]; - archivedObjects.wrapperArray[0] = new Wrapper(0); - archivedObjects.wrapperArray[1] = new Wrapper(1); - archivedObjects.wrapperArray[2] = new Wrapper(2); - } else { - restored = true; - System.out.println("Initialized from CDS"); - System.out.println("intArray " + archivedObjects.intArray); - System.out.println("charPairArray " + archivedObjects.charPairArray); - System.out.println("wrapperArray " + archivedObjects.wrapperArray); - } - - for (Integer i : archivedObjects.intArray) { - System.out.println(i); - } - - for (CharPair c : archivedObjects.charPairArray) { - System.out.println(c); - } - - for (Wrapper w : archivedObjects.wrapperArray) { - System.out.println(w); - } - } - - public static void main(String[] args) { - if (!ValueClass.isFlatArray(archivedObjects.intArray)) { - throw new RuntimeException("Integer array should be flat"); - } - - if (!ValueClass.isFlatArray(archivedObjects.charPairArray)) { - throw new RuntimeException("CharPair array should be flat"); - } - - if (!ValueClass.isFlatArray(archivedObjects.wrapperArray)) { - throw new RuntimeException("Wrapper array should be flat"); - } - - if (restored) { - // Ensure archived arrays are restored properly - Integer[] runtimeIntArray = new Integer[3]; - runtimeIntArray[0] = new Integer(0); - runtimeIntArray[1] = new Integer(1); - runtimeIntArray[2] = new Integer(2); - - CharPair[] runtimeCharPairArray = new CharPair[3]; - runtimeCharPairArray[0] = new CharPair('a', 'b'); - runtimeCharPairArray[1] = new CharPair('c', 'd'); - runtimeCharPairArray[2] = new CharPair('e', 'f'); - - Wrapper[] runtimeWrapperArray = new Wrapper[3]; - runtimeWrapperArray[0] = new Wrapper(0); - runtimeWrapperArray[1] = new Wrapper(1); - runtimeWrapperArray[2] = new Wrapper(2); - - if (Arrays.compare(archivedObjects.intArray, runtimeIntArray) != 0) { - throw new RuntimeException("Integer array not restored correctly"); - } - - if (Arrays.compare(archivedObjects.charPairArray, runtimeCharPairArray) != 0) { - throw new RuntimeException("CharPair array not restored correctly"); - } - - if (Arrays.compare(archivedObjects.wrapperArray, runtimeWrapperArray) != 0) { - throw new RuntimeException("Wrapper array not restored correctly"); - } - } - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java deleted file mode 100644 index 90838fd92be3..000000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @summary Test archived flat arrays - * @requires vm.cds.write.archived.java.heap - * @requires vm.debug - * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds - * @enablePreview - * @modules java.base/jdk.internal.value - * @compile ArchivedFlatArrayApp.java ArchivedArrayLayoutsApp.java - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar archived_flat_array.jar ArchivedFlatArrayApp - * ArchivedFlatArrayApp$ArchivedData - * ArchivedFlatArrayApp$CharPair - * ArchivedFlatArrayApp$Wrapper - * ArchivedArrayLayoutsApp - * ArchivedArrayLayoutsApp$Point - * ArchivedArrayLayoutsApp$ArchivedData - * @run main/othervm ArchivedFlatArrayTest - */ - -import jdk.test.lib.cds.CDSTestUtils; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.helpers.ClassFileInstaller; - -public class ArchivedFlatArrayTest { - - static String appJar = ClassFileInstaller.getJarPath("archived_flat_array.jar"); - static String mainClass = "ArchivedFlatArrayApp"; - static String mainClass2 = "ArchivedArrayLayoutsApp"; - - public static void test(String className, String[] classlist) throws Exception { - String[] suffix = TestCommon.list("--enable-preview", - "-Xbootclasspath/a:" + appJar, - "-XX:ArchiveHeapTestClass=" + className, - "--add-exports", - "java.base/jdk.internal.value=ALL-UNNAMED", - "-Xlog:aot+heap"); - - OutputAnalyzer output = TestCommon.dump(appJar, classlist, suffix); - output.shouldHaveExitValue(0); - output.shouldContain("Archived field " + className + "::archivedObjects"); - - output = TestCommon.exec(appJar, TestCommon.concat(suffix, className)); - output.shouldHaveExitValue(0); - output.shouldContain("init subgraph " + className); - output.shouldContain("Initialized from CDS"); - } - - public static void main(String[] args) throws Exception { - test(mainClass, TestCommon.list(mainClass, "ArchivedFlatArrayApp$ArchivedData", "ArchivedFlatArrayApp$CharPair")); - test(mainClass2, TestCommon.list(mainClass2, "ArchivedArrayLayoutsApp$ArchivedData", "ArchivedArrayLayoutsApp$Point")); - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java index f69f2be48641..4bd852750908 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,10 @@ * @summary Test primitive box caches integrity in various scenarios (IntegerCache etc) * @requires vm.cds.write.archived.java.heap * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds - * @compile --add-exports java.base/jdk.internal.misc=ALL-UNNAMED CheckIntegerCacheApp.java ArchivedIntegerHolder.java + * @compile --add-exports java.base/jdk.internal.misc=ALL-UNNAMED CheckIntegerCacheApp.java * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boxCache.jar CheckIntegerCacheApp - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boxCache-boot.jar ArchivedIntegerHolder * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:./WhiteBox.jar ArchivedIntegerCacheTest */ @@ -47,15 +46,10 @@ public class ArchivedIntegerCacheTest { private static WhiteBox WB = WhiteBox.getWhiteBox(); public static String[] mixArgs(String... args) { - String bootJar = ClassFileInstaller.getJarPath("boxCache-boot.jar"); - - String[] newArgs = new String[args.length + 5]; + String[] newArgs = new String[args.length + 2]; newArgs[0] = "--add-exports"; newArgs[1] = "java.base/jdk.internal.misc=ALL-UNNAMED"; - newArgs[2] = "-Xbootclasspath/a:" + bootJar; - newArgs[3] = "-XX:+IgnoreUnrecognizedVMOptions"; - newArgs[4] = "-XX:ArchiveHeapTestClass=ArchivedIntegerHolder"; - System.arraycopy(args, 0, newArgs, 5, args.length); + System.arraycopy(args, 0, newArgs, 2, args.length); return newArgs; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java index 5068d203a916..32f305c0d96f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java @@ -65,14 +65,6 @@ public static void main(String[] args) throws Exception { } } - // Check that archived integer cache agrees with runtime integer cache. - for (int i = -128; i <= 127; i++) { - if (ArchivedIntegerHolder.archivedObjects[i + 128] != Integer.valueOf(i)) { - throw new RuntimeException( - "FAILED. Archived and runtime caches disagree for " + i); - } - } - int high = Integer.parseInt(args[0]); if (Integer.valueOf(high) != Integer.valueOf(high)) { throw new RuntimeException( diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java index 41fa1a6493c8..120a7315c7a2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java @@ -84,6 +84,7 @@ public class ModulePathAndFMG { private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar"; private static String TEST_FROM_CDS = "class,load.*com.foos.Test.*shared objects file"; private static String MAP_FAILED = "Unable to use shared archive"; + private static String NON_JAR_FILES = "module path contains sub-directories or non-JAR files"; private static String PATH_SEPARATOR = File.pathSeparator; private static String appClasses[] = {MAIN_CLASS, TEST_CLASS}; private static String prefix[] = {"-Djava.class.path=", "-Xlog:cds,class+load,class+path=info"}; @@ -116,9 +117,6 @@ public static void buildTestModule() throws Exception { dupDir = Files.createTempDirectory(USER_DIR, DUP_LIBS); dupJar = dupDir.resolve(DUP_MODULE + ".jar"); Files.copy(testJar, dupJar, StandardCopyOption.REPLACE_EXISTING); - - badJar = libsDir.resolve(MAIN_MODULE + ".JAR"); - Files.copy(mainJar, badJar, StandardCopyOption.REPLACE_EXISTING); } public static void buildJmod() throws Exception { @@ -136,6 +134,9 @@ public static void buildJmod() throws Exception { public static void main(String... args) throws Exception { runWithModulePath(); runWithExplodedModule(); + + badJar = libsDir.resolve(MAIN_MODULE + ".JAR"); + Files.copy(mainJar, badJar, StandardCopyOption.REPLACE_EXISTING); runWithJmodAndBadJar(); } @@ -329,18 +330,22 @@ public static void runWithJmodAndBadJar() throws Exception { .assertAbnormalExit(out -> { out.shouldContain(FMG_DISABLED) .shouldNotContain(FMG_ENABLED) + .shouldContain(NON_JAR_FILES) .shouldContain(FIND_EXCEPTION_MESSAGE); }); runModulePath += PATH_SEPARATOR + testJar.toString(); + + // non-jar files in runtime --module is incompatible with FMG tty("12. run with CDS on, with module path com.bars.jar:com.foos.jmod:com.foos.jar"); TestCommon.runWithModules(prefix, null, // --upgrade-module-path runModulePath, // --module-path MAIN_MODULE) // -m .assertNormalExit(out -> { - out.shouldNotContain(FMG_DISABLED) - .shouldContain(FMG_ENABLED) + out.shouldContain(FMG_DISABLED) + .shouldNotContain(FMG_ENABLED) + .shouldContain(NON_JAR_FILES) .shouldMatch(TEST_FROM_CDS) .shouldMatch(MAIN_FROM_CDS) .shouldContain(CLASS_FOUND_MESSAGE); @@ -355,6 +360,7 @@ public static void runWithJmodAndBadJar() throws Exception { .assertAbnormalExit(out -> { out.shouldContain(FMG_DISABLED) .shouldNotContain(FMG_ENABLED) + .shouldContain(NON_JAR_FILES) .shouldMatch(MODULE_NOT_RECOGNIZED); }); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java index 2674793c04ac..d71a157fe576 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java @@ -27,14 +27,18 @@ * @summary Test jvmti class file loader hook interaction with AppCDS * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @requires vm.cds + * @requires vm.cds.supports.aot.class.linking * @requires vm.jvmti * @build ClassFileLoadHook + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/native ClassFileLoadHookTest */ import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.cds.CDSAppTester; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.helpers.ClassFileInstaller; @@ -46,12 +50,15 @@ public class ClassFileLoadHookTest { "java/sql/SQLException" }; + static final String mainClass = "ClassFileLoadHook"; + static String wbJar; + static String appJar; + static String useWb; + public static void main(String[] args) throws Exception { - String wbJar = - ClassFileInstaller.writeJar("WhiteBox.jar", "jdk.test.whitebox.WhiteBox"); - String appJar = - ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses); - String useWb = "-Xbootclasspath/a:" + wbJar; + wbJar = ClassFileInstaller.writeJar("WhiteBox.jar", "jdk.test.whitebox.WhiteBox"); + appJar = ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses); + useWb = "-Xbootclasspath/a:" + wbJar; // First, run the test class directly, w/o sharing, as a baseline reference CDSOptions opts = (new CDSOptions()) @@ -61,7 +68,7 @@ public static void main(String[] args) throws Exception { "-XX:+WhiteBoxAPI", useWb, "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", - "ClassFileLoadHook", + mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON); CDSTestUtils.run(opts) .assertNormalExit(); @@ -71,7 +78,7 @@ public static void main(String[] args) throws Exception { OutputAnalyzer out = TestCommon.exec(appJar, "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", useWb, - "ClassFileLoadHook", + mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_OFF); TestCommon.checkExec(out); @@ -82,7 +89,7 @@ public static void main(String[] args) throws Exception { "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", useWb, "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", - "ClassFileLoadHook", + mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_AUTO_CFLH_ON); opts = (new CDSOptions()).setXShareMode("auto"); @@ -93,27 +100,69 @@ public static void main(String[] args) throws Exception { "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", useWb, "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", - "ClassFileLoadHook", + mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON); TestCommon.checkExec(out); // JEP 483: if dumped with -XX:+AOTClassLinking, cannot use archive when CFLH is enabled - TestCommon.testDump(appJar, sharedClasses, useWb, "-XX:+AOTClassLinking"); - out = TestCommon.exec(appJar, - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", useWb, - "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", - "-Xlog:aot", - "-Xlog:cds", - "ClassFileLoadHook", - "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON); - if (out.contains("Using AOT-linked classes: false (static archive: no aot-linked classes")) { - // JTREG is executed with VM options that do not support -XX:+AOTClassLinking, so - // the static archive was not created with aot-linked classes. - out.shouldHaveExitValue(0); - } else { - out.shouldContain("shared archive file has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use."); - out.shouldNotHaveExitValue(0); + Tester t = new Tester(); + t.setCheckExitValue(false); + t.runAOTWorkflow(); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + if (runMode == RunMode.TRAINING) { + return new String[] { + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "-XX:+AOTClassLinking", + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "-Xlog:aot,cds" + }; + } else if (runMode == RunMode.ASSEMBLY) { + return new String[] { + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "-XX:+AOTClassLinking", + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "-Xlog:aot,cds" + }; + } else { + return new String[] { + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "-Xlog:aot,cds" + }; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + if (runMode == RunMode.TRAINING) { + return new String[] { mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON }; + } else { + return new String[] { mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON }; + } + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode == RunMode.PRODUCTION) { + out.shouldContain("AOT cache has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use."); + out.shouldNotHaveExitValue(0); + } } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java index ea2c6fc88b49..2fc58d7fc0d4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java @@ -62,7 +62,6 @@ * @test id=aot * @summary Dump time resolution of constant pool entries (AOT workflow). * @requires vm.cds - * @requires vm.cds.supports.aot.class.linking * @requires vm.compMode != "Xcomp" * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/ * @build OldProvider OldClass OldConsumer StringConcatTestOld @@ -89,7 +88,7 @@ public class ResolvedConstants { static boolean aotClassLinking; public static void main(String[] args) throws Exception { test(args, false); - if (!args[0].equals("DYNAMIC")) { + if (args[0].equals("AOT")) { test(args, true); } } diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java index afbe6e2182fc..e792fea3422f 100644 --- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java @@ -81,10 +81,10 @@ static void testCmd(String arg, int expectExitCode, String... expectStrings) thr } public static void main(String args[]) throws Exception { - testCmd("foo/bar", 0, ""); + testCmd("foo.bar", 0, ""); testCmd("", 1, "IllegalArgumentException", "mandatory"); - testCmd("java/lang/Object", 0, "java/lang/Object", "@bootstrap"); - testCmd("java/lang/Class", 0, "java/lang/Class", "@bootstrap"); - testCmd("runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd$Line", 0, "@app", "p1", "p2"); + testCmd("java.lang.Object", 0, "java.lang.Object", "@bootstrap"); + testCmd("java.lang.Class", 0, "java.lang.Class", "@bootstrap"); + testCmd("runtime.valhalla.inlinetypes.ClassPrintLayoutDcmd$Line", 0, "@app", "p1", "p2"); } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java index bc69bd971de3..f5ddb642559e 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.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 @@ -118,11 +118,29 @@ static void localSleep() { } public static void infinite() {} public static void throwable() { - throw new RuntimeException("throwable called"); + throw new RuntimeException("throwable called"); /* line 13 */ } } """; + private static boolean matchSource(String source) { + // The first version is in this file, the second version was cleaned up. + return source == null || + source.equals("RedefineRunningMethodsWithBacktrace.java") || + source.equals("RedefineRunningMethodsWithBacktrace_B.java"); + } + + static final int firstLineNumber = 82; + static final int cleanedupLineNumber = -1; + static final int rerunLineNumber = 13; + + private static boolean matchLineNumber(int lineNumber) { + // Line number of the throw in the each version of the redefined class. + // The first version of this method is running so we have the line number, + // the second is cleaned up, so we don't, the third version is current so we do. + return lineNumber == firstLineNumber || lineNumber == cleanedupLineNumber || lineNumber == rerunLineNumber; + } + private static void touchRedefinedMethodInBacktrace(Throwable throwable) { System.out.println("touchRedefinedMethodInBacktrace: "); throwable.printStackTrace(); // this actually crashes with the bug in @@ -131,8 +149,24 @@ private static void touchRedefinedMethodInBacktrace(Throwable throwable) { // Make sure that we can convert the backtrace, which is referring to // the redefined method, to a StrackTraceElement[] without crashing. StackTraceElement[] stackTrace = throwable.getStackTrace(); - for (int i = 0; i < stackTrace.length; i++) { - StackTraceElement frame = stackTrace[i]; + StackTraceElement frame = stackTrace[0]; + assertEquals(frame.getClassName(), "RedefineRunningMethodsWithBacktrace_B", + "\nTest failed: trace[0].getClassName() returned " + frame.getClassName()); + assertEquals(frame.getMethodName(), "throwable", + "\nTest failed: trace[0].getMethodName() returned " + frame.getMethodName()); + assertTrue(matchSource(frame.getFileName()), + "\nTest failed: trace[0].getFileName() returned " + frame.getFileName()); + assertTrue(matchLineNumber(frame.getLineNumber()), + "\nTest failed: trace[0].getLineNumber() returned " + frame.getLineNumber()); + + frame = stackTrace[1]; + assertEquals(frame.getClassName(), "RedefineRunningMethodsWithBacktrace", + "\nTest failed: trace[1].getClassName() returned " + frame.getClassName()); + assertEquals(frame.getMethodName(), "getThrowableInB", + "\nTest failed: trace[1].getMethodName() returned " + frame.getMethodName()); + + for (int i = 2; i < stackTrace.length; i++) { + frame = stackTrace[i]; assertNotNull(frame.getClassName(), "\nTest failed: trace[" + i + "].getClassName() returned null"); assertNotNull(frame.getMethodName(), diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java new file mode 100644 index 000000000000..8e9410cdb8d9 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.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. + * + * 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 8381564 + * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless + * @summary Test that different ways to avoid executing main() are reported as correctly as failure. + * @library /test/lib /testlibrary_tests / + * @run driver ${test.main.class} + */ + +package testlibrary_tests.ir_framework.tests; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class TestNoMainExecution { + public static void main(String[] args) { + // We do not handshake because this flag disables socket communication + run("-DReproduce=true"); + + // We do not reach main() because there is no source file involved. + run("--version"); + + // We do not reach main() because we crash already at start-up. + runWithVmCrash(); + } + + private static void run(String... flags) { + try { + TestFramework.runWithFlags(flags); + Asserts.fail("should throw"); + } catch (RuntimeException e) { + String errorMessage = e.getMessage(); + // We expect a useful help message - match its header. + Asserts.assertTrue(errorMessage.contains("Did any of the following happen?"), errorMessage); + } + } + + private static void runWithVmCrash() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream oldErr = System.err; + + try (PrintStream ps = new PrintStream(baos)) { + System.setErr(ps); + + try { + TestFramework.runWithFlags("-Xcomp", "-XX:+CICountNative", "-XX:CICrashAt=1"); + Asserts.fail("should throw"); + } catch (RuntimeException e) { + // With a VM crash, the message is found on the normal stderr instead. + System.setErr(oldErr); + String output = baos.toString(); + Asserts.assertTrue(output.contains("Did any of the following happen?")); + } + } + } + + @Test + public void test() {} +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java index 7de32d1bc10b..ccea11078c13 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java @@ -43,19 +43,11 @@ import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.DataName; import compiler.lib.template_framework.StructuralName; -import static compiler.lib.template_framework.Template.scope; -import static compiler.lib.template_framework.Template.transparentScope; -import static compiler.lib.template_framework.Template.hashtagScope; -import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.fuel; -import static compiler.lib.template_framework.Template.addDataName; -import static compiler.lib.template_framework.Template.dataNames; -import static compiler.lib.template_framework.Template.addStructuralName; -import static compiler.lib.template_framework.Template.structuralNames; + import static compiler.lib.template_framework.DataName.Mutability.MUTABLE; import static compiler.lib.template_framework.DataName.Mutability.IMMUTABLE; import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE; +import static compiler.lib.template_framework.Template.*; import compiler.lib.template_framework.library.Hooks; @@ -71,6 +63,7 @@ public static void main(String[] args) { comp.addJavaSourceCode("p.xyz.InnerTest3", generateWithHashtagAndDollarReplacements()); comp.addJavaSourceCode("p.xyz.InnerTest3b", generateWithHashtagAndDollarReplacements2()); comp.addJavaSourceCode("p.xyz.InnerTest3c", generateWithHashtagAndDollarReplacements3()); + comp.addJavaSourceCode("p.xyz.InnerTest12", generateScopeBuildingUtilityMethods()); comp.addJavaSourceCode("p.xyz.InnerTest4", generateWithCustomHooks()); comp.addJavaSourceCode("p.xyz.InnerTest5", generateWithLibraryHooks()); comp.addJavaSourceCode("p.xyz.InnerTest6", generateWithRecursionAndBindingsAndFuel()); @@ -95,6 +88,7 @@ public static void main(String[] args) { comp.invoke("p.xyz.InnerTest3", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3b", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest3c", "main", new Object[] {}); + comp.invoke("p.xyz.InnerTest12", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest4", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest5", "main", new Object[] {}); comp.invoke("p.xyz.InnerTest6", "main", new Object[] {}); @@ -482,6 +476,127 @@ public static void main() { return templateClass.render(); } + + // The following examples show how to use the additional utility methods to build scopes in a top down readable way. + // Note that the code was specially indented with spaces such that the generated code is nicely formatted while the + // generating source code remains easy to read. + static String generateScopeBuildingUtilityMethods() { + return Template.make(() -> scope( + """ + package p.xyz; + + public class InnerTest12 { + int iFld; + static int iFld0; + static int iFld1; + static int iFld2; + boolean flagA; + boolean flagB; + boolean flagC; + + public static void main() { + var t = new InnerTest12(); + t.testRepeat1(); + t.testRepeat2(); + t.testRepeatAndJoin(); + t.testJoin(1, 2, 3); + } + + void testRepeat1() { + """, + + // Assume you want to repeat incrementing iFld. You could either duplicate the lines: + " iFld += 3;\n", + " iFld += 3;\n", + " iFld += 3;\n", + // Or you can use the repeat() utility method to achieve the same task: + repeat(3, scope( + " iFld += 5;\n")), + """ + } + + """, + + // Sometimes repeats should be slightly different, for example, to generate code. Let's assume you want + // to create a couple of equally looking classes. You can use the indexed repeat() utility method. + // Let's create vie classes: + repeat(5, i -> scope( + " class MyClass", i, "{}\n")), + """ + + void testRepeat2() { + """, + // And then create an instance for each of them: + repeat(5, i -> scope( + " new MyClass", i, "();\n")), + // Or you directly define 'i' with let to use #-replacements (you can, of course, also define more + // variables with let()): + repeat(5, i -> scope( + let("i", i), + " new MyClass#i();\n")), + """ + } + + """, + + // Let's assume that you want to put some varying strings into the same scope multiple times. We can use + // the map() utility method to map a string to a scope. For exapmle, you could define your class names + // upfront and use them directly: + map(List.of("One", "Two", "Three"), klass -> scope( + let("klass", klass), + " class #klass{}\n")), + "\n", + // If you still need an incrementing index, for example, to also generate different field accesses, + // you can use the indexed map() version: + map(List.of("Four", "Five", "Six"), (klass, fieldIndex) -> scope( + let("klass", klass), + let("fieldIndex", fieldIndex), + """ + class #klass{ + void test() { + InnerTest12.iFld#fieldIndex = 34; // Assign to a specific static field + } + } + + """)), + + // Suppose you have a method with 5 int args: + " void fiveInts(int a, int b, int c, int d, int e) {}\n", + // and want to call it with all zeros. You could be tempted to use repeat() with "0 ,", but what about + // the last comma? You need to omit it, otherwise it will fail to compile. What we need here is a join + // and not a bare concat of repeated scopes. We can use the repeatAndJoin() utility method for that: + """ + + void testRepeatAndJoin() { + """, + " fiveInts(", repeatAndJoin(5, ", ", scope("0")), ");\n", + + // If we want to have some ascending numbers, we can use the indexed repeatAndJoin() version: + " fiveInts(", repeatAndJoin(5, ", ", index -> scope("" + index)), ");\n", + + // Let's assume you want to AND several conditions together. You can use the utility method mapAndJoin() + // that maps and then joins the resulting scopes together: + " if (", mapAndJoin(List.of("flagA", "flagB", "flagC"), " && ", flag -> scope(flag)), ") {\n", + """ + } + } + + """, + + // You can also use the indexed version of mapAndJoin() which is useful, for example, to define a method + // with different parameter names: + " void testJoin(", + mapAndJoin(List.of("int", "float", "long"), ", ", (type, index) -> scope( + let("type", type), + let("index", index), + "#type x#index")), ") {}\n", + """ + } + """ + )).render(); + } + + // In this example, we look at the use of Hooks. They allow us to reach back, to outer // scopes. For example, we can reach out from inside a method body to a hook anchored at // the top of the class, and insert a field. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java index f56a9d5b2311..a05b2f6d8f03 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java @@ -34,9 +34,7 @@ package template_framework.tests; -import java.util.Arrays; import java.util.List; -import java.util.HashSet; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.DataName; @@ -44,22 +42,11 @@ import compiler.lib.template_framework.Hook; import compiler.lib.template_framework.TemplateBinding; import compiler.lib.template_framework.RendererException; -import static compiler.lib.template_framework.Template.scope; -import static compiler.lib.template_framework.Template.transparentScope; -import static compiler.lib.template_framework.Template.nameScope; -import static compiler.lib.template_framework.Template.hashtagScope; -import static compiler.lib.template_framework.Template.setFuelCostScope; -import static compiler.lib.template_framework.Template.$; -import static compiler.lib.template_framework.Template.let; -import static compiler.lib.template_framework.Template.fuel; -import static compiler.lib.template_framework.Template.setFuelCost; -import static compiler.lib.template_framework.Template.addDataName; -import static compiler.lib.template_framework.Template.dataNames; -import static compiler.lib.template_framework.Template.addStructuralName; -import static compiler.lib.template_framework.Template.structuralNames; + import static compiler.lib.template_framework.DataName.Mutability.MUTABLE; import static compiler.lib.template_framework.DataName.Mutability.IMMUTABLE; import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE; +import static compiler.lib.template_framework.Template.*; /** * The tests in this file are mostly there to ensure that the Template Rendering @@ -171,6 +158,14 @@ public static void main(String[] args) { testHookAndScopes1(); testHookAndScopes2(); testHookAndScopes3(); + testRepeat(); + testRepeatIndexed(); + testRepeatAndJoin(); + testRepeatAndJoinIndexed(); + testMap(); + testMapIndexed(); + testMapAndJoin(); + testMapAndJoinIndexed(); // The following tests should all fail, with an expected exception and message. expectRendererException(() -> testFailingNestedRendering(), "Nested render not allowed."); @@ -3373,6 +3368,128 @@ public static void testHookAndScopes3() { checkEQ(code, expected); } + static void testRepeat() { + var templateString = Template.make(() -> scope( + repeat(3, scope("repeat\n")) + )).render(); + + String expected = + """ + repeat + repeat + repeat + """; + checkEQ(templateString, expected); + } + + static void testRepeatIndexed() { + var templateString = Template.make(() -> scope( + repeat(2, index -> scope("1: scope for index " + index +"\n")), + repeat(3, index -> scope("2: scope for index " + index +"\n")) + )).render(); + + String expected = + """ + 1: scope for index 0 + 1: scope for index 1 + 2: scope for index 0 + 2: scope for index 1 + 2: scope for index 2 + """; + checkEQ(templateString, expected); + } + + static void testRepeatAndJoin() { + var templateString = Template.make(() -> scope( + repeatAndJoin(2, ",\n", scope("1: scope")), + "\n\n", + repeatAndJoin(3, ",\n", scope("2: scope")), + "\n" + )).render(); + + String expected = + """ + 1: scope, + 1: scope + + 2: scope, + 2: scope, + 2: scope + """; + checkEQ(templateString, expected); + } + + static void testRepeatAndJoinIndexed() { + var templateString = Template.make(() -> scope( + repeatAndJoin(2, ",\n", index -> scope("1: scope for index " + index)), + "\n\n", + repeatAndJoin(3, ",\n", index -> scope("2: scope for index " + index)), + "\n" + )).render(); + + String expected = + """ + 1: scope for index 0, + 1: scope for index 1 + + 2: scope for index 0, + 2: scope for index 1, + 2: scope for index 2 + """; + checkEQ(templateString, expected); + } + + static void testMap() { + var templateString = Template.make(() -> scope( + map(List.of(1, 2, 3), element -> scope("Element " + element +"\n")) + )).render(); + + String expected = + """ + Element 1 + Element 2 + Element 3 + """; + checkEQ(templateString, expected); + } + + static void testMapIndexed() { + var templateString = Template.make(() -> scope( + map(List.of(1, 2, 3), (element, index) -> scope("Element " + element + " at index " + index + "\n")) + )).render(); + + String expected = + """ + Element 1 at index 0 + Element 2 at index 1 + Element 3 at index 2 + """; + checkEQ(templateString, expected); + } + + static void testMapAndJoin() { + var templateString = Template.make(() -> scope( + mapAndJoin(List.of(1, 2, 3), ", ", (element) -> scope("Element " + element)) + )).render(); + + String expected = "Element 1, Element 2, Element 3"; + checkEQ(templateString, expected); + } + + static void testMapAndJoinIndexed() { + var templateString = Template.make(() -> scope( + mapAndJoin(List.of(1, 2, 3), ", ", + (element, index) -> scope("Element " + element + " at index " + index)) + )).render(); + + String expected = "Element 1 at index 0, Element 2 at index 1, Element 3 at index 2"; + checkEQ(templateString, expected); + } + + /* + * Failing tests start here + */ + public static void testFailingNestedRendering() { var template1 = Template.make(() -> scope( "alpha\n" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java index 20c9778f2510..90982a167bc5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java @@ -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 @@ -80,6 +80,7 @@ public static void main (String argv[]) { static final String DEBUGGEE_CLASS = TEST_CLASS + "a"; static final String FIRST_BREAK = DEBUGGEE_CLASS + ".main"; static final String LAST_BREAK = DEBUGGEE_CLASS + ".breakHere"; + static final String THREAD_STARTED_BREAK = DEBUGGEE_CLASS + ".threadStarted"; static final String SUSPENDED = "Suspended"; static final String DEBUGGEE_THREAD = PACKAGE_NAME + "." + SUSPENDED; @@ -94,9 +95,9 @@ protected void runCases() { String[] threads; jdb.setBreakpointInMethod(LAST_BREAK); - reply = jdb.receiveReplyFor(JdbCommand.cont); + waitForTestedThreadStarts(THREAD_STARTED_BREAK, 2); while (true) { - threads = jdb.getThreadIds(DEBUGGEE_THREAD); + threads = jdb.getThreadIdsByName(SUSPENDED); if (threads.length != 1) { log.complain("jdb should report 1 instance of " + DEBUGGEE_THREAD); log.complain("Found: " + threads.length); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java index 35a4d4258260..dc518cfdbd63 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java @@ -23,6 +23,8 @@ package nsk.jdb.suspend.suspend001; +import jdk.test.lib.thread.ThreadWrapper; + import nsk.share.*; import nsk.share.jpda.*; import nsk.share.jdb.*; @@ -42,6 +44,8 @@ public static void main(String args[]) { static void breakHere () {} + static void threadStarted () {} + static Object lock = new Object(); static Object waitnotify = new Object(); public static volatile int notSuspended = 0; @@ -50,8 +54,8 @@ public int runIt(String args[], PrintStream out) { argumentHandler = new JdbArgumentHandler(args); log = new Log(out, argumentHandler); - Thread suspended = new Suspended("Suspended"); - Thread myThread = new MyThread("MyThread"); + Thread suspended = new Suspended("Suspended").getThread(); + Thread myThread = new MyThread("MyThread").getThread(); // lock monitor to prevent threads from finishing after they started synchronized (lock) { @@ -103,16 +107,16 @@ public static int getResult() { } } -// This test uses a platform thread because suspending the tested virtual thread -// and continuing causes jdb to stop responding. -class Suspended extends Thread { +class Suspended extends ThreadWrapper { String name; public Suspended (String n) { + super(n); name = n; } public void run() { + suspend001a.threadStarted(); // Concatenate strings in advance to avoid lambda calculations later final String ThreadFinished = "Thread finished: " + this.name; suspend001a.log.display("Thread started: " + this.name); @@ -129,14 +133,16 @@ public void run() { } } -class MyThread extends Thread { +class MyThread extends ThreadWrapper { String name; public MyThread (String n) { + super(n); name = n; } public void run() { + suspend001a.threadStarted(); // Concatenate strings in advance to avoid lambda calculations later final String ThreadFinished = "Thread finished: " + this.name; suspend001a.log.display("Thread started: " + this.name); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java index 201995bde3f0..51b5c3918ad8 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface Accessible works fine with * the ArrayType sub-interface. */ -public class accipp001 extends Log { +public class accipp001 { final static boolean MODE_VERBOSE = false; /** The main class names of the debugger & debugee applications. */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java index c85fa1c0a2a0..d945672439fd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ * for ArrayType, ClassType, InterfaceType */ -public class isPrivate001 extends Log { +public class isPrivate001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java index b1d5cfdf199e..49780d19b271 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ * for ArrayType, ClassType, InterfaceType */ -public class isProtected001 extends Log { +public class isProtected001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java index d4883f7573c5..0238dc5e6a06 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ * for ArrayType, ClassType, InterfaceType */ -public class isPublic001 extends Log { +public class isPublic001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java index a749568ad4c9..a8d22483ad87 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java @@ -37,7 +37,7 @@ * for ClassType, InterfaceType */ -public class modifiers001 extends Log { +public class modifiers001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java index 9132b986a079..7bc941afc71f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ * a target VM via com.sun.jdi.SharedMemoryAttach connector.
      * The test also analyzes exit code of debugee's process. */ -public class attach002 extends Log { +public class attach002 { static final int PASSED = 0; static final int FAILED = 2; static final int JCK_STATUS_BASE = 95; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java index bc093a807ba7..501f38358d86 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ClassObjectReference of com.sun.jdi package */ -public class reflectype001 extends Log { +public class reflectype001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java index 227d67ab4ea7..ceb8015b5b37 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,11 @@ * of the JDI interface ClassObjectReference of com.sun.jdi package */ -public class reflectype002 extends Log { +public class reflectype002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -79,7 +80,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java index fb1b2f683ed0..b627f40f8178 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allfields001 extends Log { +public class allfields001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -148,7 +149,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -167,11 +168,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java index ae8e7ca3d6c1..ee94fd88bc42 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allfields002 extends Log { +public class allfields002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java index c97278309c33..0ee77b3aa62e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allfields003 extends Log { +public class allfields003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) { verbose_mode = argHandler.verbose(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java index 96cb9d2924d5..d47c016a7251 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allfields004 extends Log { +public class allfields004 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for class without any declarated fields\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java index 0a15a520f3e5..adba1920a95b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allmethods001 extends Log { +public class allmethods001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -203,7 +204,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -222,11 +223,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java index 7e0757957b42..856366d67e0d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allmethods002 extends Log { +public class allmethods002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java index 38039d69767c..5bf40e7c57b1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allmethods003 extends Log { +public class allmethods003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java index 681bf7af8bb9..1a06a9a55bed 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class allmethods004 extends Log { +public class allmethods004 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for class without any methods\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java index 1948b32cdc49..eb8b3e097da0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class classobj001 extends Log { +public class classobj001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -104,7 +105,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -123,11 +124,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for ArraType, ClassType, InterfaceType\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java index 82322f250e1f..7fcc0796f247 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class classobj002 extends Log { +public class classobj002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -79,7 +80,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java index 096d1c24ca5a..78dc82ad6ee5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,9 +37,10 @@ * for ClassType, InterfaceType */ -public class failedToInitialize001 extends Log { +public class failedToInitialize001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -93,7 +94,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -112,11 +113,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for ClassType, InterfaceType\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java index 1bfb90ff309b..7319373724a1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class fieldbyname001 extends Log { +public class fieldbyname001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -141,7 +142,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -160,11 +161,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java index 32eea48a50f0..7721afec2a2a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class fieldbyname002 extends Log { +public class fieldbyname002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java index a8c7f6f35fc9..572668ca5e5f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class fieldbyname003 extends Log { +public class fieldbyname003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -81,7 +82,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -107,11 +108,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java index d7638379138a..d496dfcf5292 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,9 +37,10 @@ * for ClassType, InterfaceType */ -public class isAbstract001 extends Log { +public class isAbstract001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -91,7 +92,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -110,11 +111,9 @@ private int runThis (String argv[], PrintStream out) { verbose_mode = argHandler.verbose(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java index f0bb211b7da3..94b2bba78b2c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ * for ClassType, InterfaceType */ -public class isVerified001 extends Log { +public class isVerified001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; @@ -97,7 +97,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + logHandler.display(message); } /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java index b059903782d1..8d85373b68e7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_s001 extends Log { +public class methbyname_s001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -183,7 +184,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -202,11 +203,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java index e09a6e384ada..57663947dd15 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_s002 extends Log { +public class methbyname_s002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java index ef3885f6e5f7..d9a7aa3becf9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_s003 extends Log { +public class methbyname_s003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java index 47bb9b350f3c..4d7892f4c903 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_s004 extends Log { +public class methbyname_s004 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -109,7 +110,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -128,11 +129,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for overloaded methods\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java index 08da95dd7fd8..dae3e3c06cf9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_ss001 extends Log { +public class methbyname_ss001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -196,7 +197,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -215,11 +216,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" ReferenceType interface of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java index e3090542cdd3..4fdd23afb729 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class methbyname_ss002 extends Log { +public class methbyname_ss002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java index cf6f4ae420f8..b42249bf6980 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibfield001 extends Log { +public class visibfield001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -135,7 +136,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -154,11 +155,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java index a9cb1fb4f412..1e11aac9eba6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibfield002 extends Log { +public class visibfield002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java index 14520271c172..e730099bb7bc 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibfield003 extends Log { +public class visibfield003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java index 5eee5228479f..78e0a925369c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibfield004 extends Log { +public class visibfield004 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for class without visible fields\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java index d3bcc84c7e29..6c5cc8de830d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibmethod001 extends Log { +public class visibmethod001 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -181,7 +182,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -200,11 +201,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java index 5f3f9e1d6e11..b8c2d032c315 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibmethod002 extends Log { +public class visibmethod002 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java index 94f8616e6274..ee6bde142b55 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibmethod003 extends Log { +public class visibmethod003 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } private static void print_log_without_verbose(String message) { @@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) { argv = argHandler.getArguments(); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = debugee.createIOPipe(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java index ba1449d960ed..8711a559c32a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibmethod004 extends Log { +public class visibmethod004 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for class without visible methods\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java index 0c752e720c3f..28bec81d290d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,10 +36,11 @@ * of the JDI interface ReferenceType of com.sun.jdi package */ -public class visibmethod005 extends Log { +public class visibmethod005 { static java.io.PrintStream out_stream; static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static // - for more easy failure evaluation + static Log log; /** The main class names of the debugger & debugee applications. */ private final static String @@ -90,7 +91,7 @@ public static int run (String argv[], PrintStream out) { } private void print_log_on_verbose(String message) { - display(message); + log.display(message); } /** @@ -109,11 +110,9 @@ private int runThis (String argv[], PrintStream out) { out_stream.println(" of the com.sun.jdi package for multiple inherited abstract methods\n"); String debugee_launch_command = debugeeName; - if (verbose_mode) { - logTo(out_stream); - } + log = new Log(out_stream, argHandler); - Binder binder = new Binder(argHandler,this); + Binder binder = new Binder(argHandler, log); Debugee debugee = binder.bindToDebugee(debugee_launch_command); IOPipe pipe = new IOPipe(debugee); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java index 99467fc03341..24711527606f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java @@ -144,25 +144,14 @@ public static String getLevelsString() { ///////////////////////////////////////////////////////////////// - /** - * Create new Log's only with Log(out) or with - * Log(out,argsHandler) constructors. - * - * @deprecated Extending test class with Log is obsolete. - */ - @Deprecated - protected Log() { - // Don't log exceptions from this method. It would just add unnecessary logs. - loggedExceptions.add("nsk.share.jdi.SerialExecutionDebugger.executeTests"); - } - /** * Incarnate new Log for the given stream and * for non-verbose mode. */ public Log(PrintStream stream) { - this(); + // Don't log exceptions from this method. It would just add unnecessary logs. + loggedExceptions.add("nsk.share.jdi.SerialExecutionDebugger.executeTests"); out = stream; } @@ -337,19 +326,6 @@ private void logExceptionForFailureAnalysis(String msg) { ///////////////////////////////////////////////////////////////// - /** - * Redirect log to the given stream. - * - * @deprecated This method is obsolete. - */ - @Deprecated - protected synchronized void logTo(PrintStream stream) { - if (out != null) { - out.flush(); - } - out = stream; - } - ///////////////////////////////////////////////////////////////// /** diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java index 98481fdf87ed..e7ff1dbba1c9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.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 @@ -119,15 +119,18 @@ public void run() { test.run(); LockSupport.parkNanos(1); } - } catch (OutOfMemoryError oom) { - if (test instanceof OOMStress) { - // Test stressing OOM, not a failure. - log.info("Caught OutOfMemoryError in OOM stress test, omitting exception."); + } catch (Throwable t) { + if (test instanceof OOMStress && isCausedByOOM(t)) { + // Test stressing OOM, not a failure. The OOME may arrive + // wrapped in another exception. + try { + log.info("Caught " + t + " in OOM stress test, omitting exception."); + } catch (OutOfMemoryError oom) { + // no memory left to log, still not a failure + } } else { - failWithException(oom); + failWithException(t); } - } catch (Throwable t) { - failWithException(t); } finally { waitForOtherThreads(); stresser.finish(); @@ -149,6 +152,16 @@ private void waitForOtherThreads() { } } + private static boolean isCausedByOOM(Throwable t) { + while (t != null) { + if (t instanceof OutOfMemoryError) { + return true; + } + t = t.getCause(); + } + return false; + } + private void failWithException(Throwable t) { log.debug("Exception in "); log.debug(test); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java b/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java index 2b217a368c25..5b8763592742 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import nsk.share.ClassUnloader; import nsk.share.TestFailure; import nsk.share.gc.ThreadedGCTest; import nsk.share.gc.gp.GarbageUtils; +import nsk.share.gc.OOMStress; import nsk.share.test.ExecutionController; import nsk.share.test.LocalRandom; @@ -99,7 +99,7 @@ ClassLoader[] createClassLoadersInternal() { } volatile ClassLoader[] currentClassLoaders; - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private ClassLoader loader; private String[] names; diff --git a/test/jdk/com/sun/jdi/BacktraceFieldTest.java b/test/jdk/com/sun/jdi/BacktraceFieldTest.java index 294b858e2f71..d3d2b137c1a1 100644 --- a/test/jdk/com/sun/jdi/BacktraceFieldTest.java +++ b/test/jdk/com/sun/jdi/BacktraceFieldTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, 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 @@ -158,12 +158,10 @@ protected void runTests() throws Exception { println("BT: backTraceVal = " + backTraceVal); printval(backTraceVal, 0); - printval(backTraceVal, 1); - printval(backTraceVal, 2); - printval(backTraceVal, 3); // backtrace has 4 elements + printval(backTraceVal, 1); // backtrace has 2 elements try { - printval(backTraceVal, 4); + printval(backTraceVal, 2); } catch (Exception e) { println("Exception " + e); } diff --git a/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java b/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java new file mode 100644 index 000000000000..0b060178b6c9 --- /dev/null +++ b/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.format.Format; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* + * @test id=default + * @bug 8387853 + * @summary Tests the default value of `sun.net.httpserver.drainAmount`, + * which is 65,536. + * + * @library /test/lib + * + * @run junit/othervm + * ${test.main.class} + */ + +/* + * @test id=negative + * @bug 8387853 + * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to a + * negative value (-1, in this case) results in server closing the + * connection if the handler leaves behind an unconsumed request body + * of length bigger than 0. + * + * @library /test/lib + * + * @run junit/othervm + * -Dsun.net.httpserver.drainAmount=-1 + * ${test.main.class} + */ + +/* + * @test id=0 + * @bug 8387853 + * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to 0 + * results in server closing the connection if the handler leaves + * behind an unconsumed request body of length bigger than 0. + * + * @library /test/lib + * + * @run junit/othervm + * -Dsun.net.httpserver.drainAmount=0 + * ${test.main.class} + */ + +/* + * @test id=1 + * @bug 8387853 + * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to 1 + * results in server closing the connection if the handler leaves + * behind an unconsumed request body of length greater than or equal + * to 1. + * + * @library /test/lib + * + * @run junit/othervm + * -Dsun.net.httpserver.drainAmount=1 + * ${test.main.class} + */ + +class DrainAmountPropertyTest { + + /** + * The {@code com.sun.net.httpserver} logger anchor to avoid getting it garbage-collected. + */ + private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver"); + + static { + boolean enableLogging = System.getProperty("test.enableLogging") != null; + if (enableLogging) { + LOGGER.setLevel(Level.ALL); // 0. Set `HttpServer`'s logger to `ALL` + Logger.getLogger("") // 1. Get the root logger + .getHandlers()[0] // 2. Get its first handler (by default it's a `ConsoleHandler`) + .setLevel(Level.ALL); // 3. Sets its level to `ALL` (by default it's `INFO`) + } + } + + @Test + void test() throws Exception { + + // Read the drain amount (i.e., the maximum allowed unconsumed request body length) + var drainAmount = Integer.getInteger("sun.net.httpserver.drainAmount", 65536); + + // Create the HTTP server + var server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); + server.createContext("/", new NoContentReturningHandler()); + server.start(); + + // Create the client + var serverAddress = server.getAddress(); + try (var clientSocket = new Socket(serverAddress.getAddress(), serverAddress.getPort())) { + + if (drainAmount > 0) { + + // Send the 1st request containing a body matching the drain amount, that is, no excess. + // This should leave the connection in a reusable state. + sendRequest(clientSocket, "/?request=reusable", drainAmount - 1); + + // Send the 2nd (empty) request and verify that the connection is still usable + sendRequest(clientSocket, "/?request=still-usable", 0); + + } + + // Send requests with zero-length bodies in various forms and verify that the connection is still usable + sendRequest(clientSocket, "GET", "/?request=get-no-content-length", null); + sendRequest(clientSocket, "GET", "/?request=get-content-length-zero", 0); + sendRequest(clientSocket, "POST", "/?request=post-no-content-length", null); + sendRequest(clientSocket, "POST", "/?request=post-content-length-zero", 0); + + // Send the 3rd request containing a body exceeding the drain amount and verify the server disconnect + assertThrows(SocketException.class, () -> { + sendRequest(clientSocket, "/?request=closing", Math.max(1, drainAmount)); + // Above request might still successfully consume the response before reading the server disconnect. + // Hence, send a 4th (empty) request to ensure to observe the socket close. + sendRequest(clientSocket, "/?request=after-close", 0); + }); + + } finally { + server.stop(0); + } + + } + + private static void sendRequest( + Socket clientSocket, String requestTarget, int requestBodyLength) + throws IOException { + sendRequest(clientSocket, "POST", requestTarget, requestBodyLength); + } + + private static void sendRequest( + Socket clientSocket, String requestMethod, String requestTarget, Integer requestBodyLength) + throws IOException { + LOGGER.info("Sending request (target=%s, bodyLength=%s)".formatted(requestTarget, requestBodyLength)); + var socketOutput = clientSocket.getOutputStream(); + var request = "%s %s HTTP/1.1%s\r\n\r\n".formatted( + requestMethod, requestTarget, + requestBodyLength == null ? "" : "\r\nContent-Length: " + requestBodyLength); + socketOutput.write(request.getBytes(US_ASCII)); + if (requestBodyLength != null && requestBodyLength > 0) { + socketOutput.write(new byte[requestBodyLength]); + } + socketOutput.flush(); + var inputStream = clientSocket.getInputStream(); + assertEquals("HTTP/1.1 204 No Content", readUntilCrLf(inputStream)); + // Consume headers, including the terminating empty line + while (!readUntilCrLf(inputStream).isEmpty()); + } + + private static String readUntilCrLf(InputStream inputStream) throws IOException { + var buffer = new StringBuilder(); + var prevChar = -1; + while (true) { + int nextChar = inputStream.read(); + if (nextChar < 0) { + // Peer disconnect is not expected, escalate it + throw new SocketException("EOF after reading: " + Format.asLiteral(buffer)); + } + buffer.append((char) nextChar); + if (prevChar == '\r' && nextChar == '\n') { + break; + } + prevChar = nextChar; + } + // Drop CRLF + buffer.setLength(buffer.length() - 2); + return buffer.toString(); + } + + private static final class NoContentReturningHandler implements HttpHandler { + + @Override + public void handle(HttpExchange exchange) throws IOException { + LOGGER.info("Received request (target=%s)".formatted(exchange.getRequestURI())); + try (exchange) { + exchange.sendResponseHeaders(204, HttpExchange.RSPBODY_EMPTY); + } + } + + } + +} diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 3a5d1ef7c970..e45bb3fdbf0d 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -53,9 +53,7 @@ import java.nio.MappedByteBuffer; import java.nio.ShortBuffer; import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; +import java.nio.file.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -603,11 +601,24 @@ public void testMapZeroSize() throws IOException { } } - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void testMapCustomPath() throws IOException { - Path path = Path.of(URI.create("jrt:/")); - try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto()); + // Zip file systems do support creating file channels + // but do not support memory mapping those files + Path scratch = Path.of("testMapCustomPath"); + Files.createDirectories(scratch); + Path zipFile = scratch.resolve("test.zip"); + + try (FileSystem zipFs = FileSystems.newFileSystem(zipFile, Map.of("create", true))) { + // create test file + Path testFile = zipFs.getPath("/test_file.txt"); + Files.writeString(testFile, "testing", StandardOpenOption.CREATE_NEW); + + // now try to map it + try (FileChannel fileChannel = FileChannel.open(testFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) { + assertThrows(UnsupportedOperationException.class, + () -> fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto())); + } } } diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java index f4f9290c97d1..ef06f1048b20 100644 --- a/test/jdk/java/foreign/TestFunctionDescriptor.java +++ b/test/jdk/java/foreign/TestFunctionDescriptor.java @@ -34,10 +34,7 @@ import java.util.Optional; import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; public class TestFunctionDescriptor extends NativeTestHelper { @@ -116,16 +113,6 @@ public void testCarrierMethodType() { assertEquals(cmt, MethodType.methodType(int.class, int.class, MemorySegment.class, MemorySegment.class)); } - @Test(expectedExceptions = IllegalArgumentException.class) - public void testBadCarrierMethodType() { - FunctionDescriptor fd = FunctionDescriptor.of(C_INT, - C_INT, - MemoryLayout.structLayout(C_INT, C_INT), - MemoryLayout.sequenceLayout(3, C_INT), - MemoryLayout.paddingLayout(4)); - fd.toMethodType(); // should throw - } - @Test(expectedExceptions = IllegalArgumentException.class) public void testIllegalInsertArgNegIndex() { FunctionDescriptor fd = FunctionDescriptor.of(C_INT); diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index 2c826ec9e213..2606e0481b32 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -307,10 +307,12 @@ public void testBadByteAlignment(MemoryLayout layout, long byteAlign) { } } - @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) + @Test(dataProvider="layoutsAndAlignments") public void testBadSequenceElementAlignmentTooBig(MemoryLayout layout, long byteAlign) { - layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align - MemoryLayout.sequenceLayout(1, layout); + MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, + () -> MemoryLayout.sequenceLayout(1, elementLayout)); + assertEquals(iae.getMessage(), "Element layout size is not multiple of alignment"); } @Test(dataProvider="layoutsAndAlignments") @@ -348,15 +350,16 @@ public void testBadElementsElementSizeNotMultipleOfAlignment(MemoryLayout layout } } - @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class) + @Test(dataProvider="layoutsAndAlignments") public void testBadStruct(MemoryLayout layout, long byteAlign) { - layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align - MemoryLayout.structLayout(layout, layout); + MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, + () -> MemoryLayout.structLayout(elementLayout, elementLayout)); + assertTrue(iae.getMessage().contains("Invalid alignment constraint for member layout")); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSequenceElement() { - SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT); // Step must be != 0 PathElement.sequenceElement(3, 0); } @@ -543,4 +546,8 @@ static Stream groupLayoutStream() { ValueLayout.JAVA_LONG, ValueLayout.JAVA_DOUBLE, }; + + private static long nextPowerOfTwo(long input) { + return 1L << -Long.numberOfLeadingZeros(input - 1); + } } diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index 10916b731d5c..19f9f576ab2e 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -73,24 +73,6 @@ public void testAlignedAccess(long align) { } } - @Test(dataProvider = "alignments") - public void testUnalignedAccess(long align) { - ValueLayout layout = ValueLayout.JAVA_INT - .withOrder(ByteOrder.BIG_ENDIAN); - assertEquals(layout.byteAlignment(), 4); - ValueLayout aligned = layout.withByteAlignment(align); - try (Arena arena = Arena.ofConfined()) { - MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), aligned); - assertEquals(alignedGroup.byteAlignment(), align); - VarHandle vh = aligned.varHandle(); - MemorySegment segment = arena.allocate(alignedGroup);; - vh.set(segment.asSlice(1L), 0L, -42); - assertEquals(align, 8); //this is the only case where access is aligned - } catch (IllegalArgumentException ex) { - assertNotEquals(align, 8); //if align != 8, access is always unaligned - } - } - @Test(dataProvider = "alignments") public void testUnalignedPath(long align) { MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN); diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java index f15e038879ea..285e8ab27ea0 100644 --- a/test/jdk/java/foreign/TestSpliterator.java +++ b/test/jdk/java/foreign/TestSpliterator.java @@ -29,9 +29,7 @@ import java.lang.foreign.*; import java.lang.invoke.VarHandle; -import java.util.LinkedList; -import java.util.List; -import java.util.Spliterator; +import java.util.*; import java.util.concurrent.CountedCompleter; import java.util.concurrent.RecursiveTask; import java.util.concurrent.atomic.AtomicLong; @@ -147,13 +145,19 @@ public void testBadStreamElementSizeZero() { .elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT)); } - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void testHyperAligned() { Arena scope = Arena.ofAuto(); MemorySegment segment = scope.allocate(8, 1); // compute an alignment constraint (in bytes) which exceed that of the native segment long bigByteAlign = Long.lowestOneBit(segment.address()) << 1; - segment.elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withByteAlignment(bigByteAlign))); + MemoryLayout elementLayout = MemoryLayout.structLayout( + Collections.nCopies(Math.toIntExact(bigByteAlign), ValueLayout.JAVA_BYTE).toArray(MemoryLayout[]::new)) + .withByteAlignment(bigByteAlign); + SequenceLayout layout = MemoryLayout.sequenceLayout(2, elementLayout); + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, + () -> segment.elements(layout)); + assertEquals(iae.getMessage(), "Incompatible alignment constraints"); } static long sumSingle(long acc, MemorySegment segment) { diff --git a/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java new file mode 100644 index 000000000000..4eab3f65ab1f --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.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. + */ + +/* + * @test id=thaw_fast_full + * @bug 8390240 + * @summary Test the full thaw path when the runtime stub's compiled + * caller is marked for deoptimization + * @requires vm.continuations + * @library /test/lib /test/hotspot/jtreg + * @run main/othervm -Xcomp DeoptimizedMethodAtMonitorEnter + */ + +/* + * @test id=thaw_fast_partial + * @bug 8390240 + * @summary Test the partial thaw path when the runtime stub's compiled + * caller is marked for deoptimization + * @requires vm.debug == true & vm.continuations + * @library /test/lib /test/hotspot/jtreg + * @run main/othervm -Xcomp -XX:+ForceSingleFrameThaw DeoptimizedMethodAtMonitorEnter + */ + +/* + * @test id=thaw_slow + * @bug 8390240 + * @summary Test the slow thaw path when the runtime stub's compiled + * caller is marked for deoptimization + * @requires vm.continuations + * @library /test/lib /test/hotspot/jtreg + * @run main/othervm DeoptimizedMethodAtMonitorEnter + */ + +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.Asserts; + +public class DeoptimizedMethodAtMonitorEnter { + private static final Object lock = new Object(); + private static A receiver = new A(); + private static int result; + + public static void main(String[] args) throws Exception { + warmUp(); + Asserts.assertTrue(receiver.m() == 1, "unexpected value=" + receiver.m()); + + var started = new CountDownLatch(1); + Thread vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + foo(); + }); + + synchronized (lock) { + vthread.start(); + started.await(); + await(vthread, Thread.State.BLOCKED); + receiver = new B(); + } + + vthread.join(); + Asserts.assertTrue(result == 3, "unexpected result=" + result); + } + + public static void foo() { + synchronized (lock) { + result = receiver.m(); + } + } + + private static void warmUp() { + for (int i = 0; i < 30_000; i++) { + foo(); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + Asserts.assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } + + static class A { + int m() { + return 1; + } + } + + static class B extends A { + int m() { + return 3; + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java new file mode 100644 index 000000000000..3021ad105e73 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=default + * @bug 8389310 + * @summary Test exception propagation when caller nmethod is marked for deoptimization + * @requires vm.continuations + * @library /test/lib /test/hotspot/jtreg + * @run main/othervm -XX:CompileCommand=dontinline,*::bar DeoptimizedMethodOnException + */ + +/* + * @test id=Xcomp + * @bug 8389310 + * @summary Test exception propagation when caller nmethod is marked for deoptimization + * @requires vm.continuations + * @library /test/lib /test/hotspot/jtreg + * @run main/othervm -XX:CompileCommand=dontinline,*::bar -Xcomp DeoptimizedMethodOnException + */ + +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.Asserts; + +public class DeoptimizedMethodOnException { + private static CountDownLatch sync = new CountDownLatch(0); + private static A receiver = new A(); + private static int resultInt; + private static String resultStr; + + public static void main(String args[]) throws Exception { + warmUp(); + Asserts.assertTrue(resultInt == 1, "resultInt=" + resultInt); + Asserts.assertTrue(resultStr.equals("MyExceptionWithExtraFields"), "resultStr=" + resultStr); + + var started = new CountDownLatch(1); + Thread vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + foo(); + }); + + sync = new CountDownLatch(1); + vthread.start(); + started.await(); + // wait until vthread blocks in sync + await(vthread, Thread.State.WAITING); + receiver = new B(); + sync.countDown(); + + vthread.join(); + Asserts.assertTrue(resultInt == 3, "resultInt=" + resultInt); + Asserts.assertTrue(resultStr.equals("MyExceptionWithoutExtraFields"), "resultStr=" + resultStr); + } + + public static void foo() { + try { + bar(); + } catch (MyException e) { + resultInt = receiver.m(); + String tmp = e.getExceptionName(); + Asserts.assertTrue(tmp.length() > 0, "length=" + tmp.length()); + resultStr = tmp; + return; + } + throw new RuntimeException("Should not reach here"); + } + + public static void bar() { + try { + sync.await(); + } catch (InterruptedException ie) {} + receiver.throwException(); + } + + private static void warmUp() { + for (int i = 0; i < 30_000; i++) { + foo(); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + Asserts.assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } +} + +abstract class MyException extends RuntimeException { + abstract String getExceptionName(); +} + +class MyExceptionWithExtraFields extends MyException { + Integer l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15; + String exceptionName; + MyExceptionWithExtraFields() { + l1=1;l2=2;l3=3;l4=4;l5=5;l6=6;l7=7;l8=8;l9=9;l10=10;l11=11;l12=12;l13=13;l14=14;l15=15; + exceptionName = new String("MyExceptionWithExtraFields"); + } + @Override + String getExceptionName() { return exceptionName; } +} + +class MyExceptionWithoutExtraFields extends MyException { + String exceptionName; + MyExceptionWithoutExtraFields() { + exceptionName = new String("MyExceptionWithoutExtraFields"); + } + @Override + String getExceptionName() { return exceptionName; } +} + +class A { + int m() { + return 1; + } + void throwException() { + throw new MyExceptionWithExtraFields(); + } +} + +class B extends A { + int m() { + return 3; + } + void throwException() { + throw new MyExceptionWithoutExtraFields(); + } +} diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java index 4d6a74e760b0..723d203c93ad 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,13 +68,11 @@ * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY305 * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER - * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY + * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= HTTPSetAuthenticatorTest BASIC PROXY * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307 - * - * @author danielfuchs */ public class HTTPSetAuthenticatorTest extends HTTPTest { diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java index aa158c3b6678..6ceb281e364c 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.HexFormat; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Random; @@ -60,6 +61,7 @@ import java.util.stream.Collectors; import javax.net.ssl.SSLContext; import sun.net.www.HeaderParser; +import sun.net.www.MessageHeader; /** * A simple HTTP server that supports Digest authentication. @@ -344,10 +346,11 @@ public static HTTPTestServer createServer(HttpProtocolType protocol, Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, algorithm); final HTTPTestServer server = new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, algorithm); + server.configureAuthentication(ctxt, schemeType, authResponder, authType); impl.start(); return server; } @@ -363,12 +366,19 @@ public static HTTPTestServer createProxy(HttpProtocolType protocol, Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, null); final HTTPTestServer server = protocol == HttpProtocolType.HTTPS - ? new HttpsProxyTunnel(impl, null, delegate) + ? new HttpsProxyTunnel(impl, null, delegate, authResponder) : new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, null); + if (protocol == HttpProtocolType.HTTPS) { + server.configureAuthentication(ctxt, HttpSchemeType.NONE, + new NoAuthResponder(auth, HttpAuthType.SERVER), + HttpAuthType.SERVER); + } else { + server.configureAuthentication(ctxt, schemeType, authResponder, authType); + } impl.start(); return server; @@ -441,16 +451,16 @@ private HttpHandler createHandler(HttpSchemeType schemeType, private void configureAuthentication(HttpContext ctxt, HttpSchemeType schemeType, - HttpTestAuthenticator auth, - HttpAuthType authType, String algorithm) { + AuthResponder authResponder, + HttpAuthType authType) { switch(schemeType) { case DIGEST: // DIGEST authentication is handled by the handler. - ctxt.getFilters().add(new HttpDigestFilter(auth, authType, algorithm)); + ctxt.getFilters().add(new HttpDigestFilter(authResponder)); break; case BASIC: // BASIC authentication is handled by the filter. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case BASICSERVER: switch(authType) { @@ -458,14 +468,14 @@ private void configureAuthentication(HttpContext ctxt, // HttpServer can't support Proxy-type authentication // => we do as if BASIC had been specified, and we will // handle authentication in the handler. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case SERVER: case SERVER307: // Basic authentication is handled by HttpServer // directly => the filter should not perform // authentication again. - setContextAuthenticator(ctxt, auth); - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + setContextAuthenticator(ctxt, authResponder.authenticator); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("Invalid combination scheme=" @@ -473,7 +483,7 @@ private void configureAuthentication(HttpContext ctxt, } case NONE: // No authentication at all. - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("No such scheme: " + schemeType); @@ -485,38 +495,230 @@ private HttpHandler create300Handler(URL proxyURL, return new Http3xxHandler(proxyURL, type, code300); } - // Abstract HTTP filter class. - private abstract static class AbstractHttpFilter extends Filter { - + private static abstract class AuthResponder { final HttpAuthType authType; + final HttpTestAuthenticator authenticator; final String type; - public AbstractHttpFilter(HttpAuthType authType, String type) { + + AuthResponder(HttpTestAuthenticator authenticator, + HttpAuthType authType, + String scheme) { + this.authenticator = authenticator; this.authType = authType; - this.type = type; + this.type = authType == HttpAuthType.PROXY + ? scheme + " Proxy" + : scheme + " Server"; } - String getLocation() { - return "Location"; - } - String getAuthenticate() { + final String authenticateHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authenticate" : "WWW-Authenticate"; + ? "Proxy-Authenticate" + : "WWW-Authenticate"; } - String getAuthorization() { + final String authorizationHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authorization" : "Authorization"; + ? "Proxy-Authorization" + : "Authorization"; } - int getUnauthorizedCode() { + int unauthorizedCode() { return authType == HttpAuthType.PROXY ? HttpURLConnection.HTTP_PROXY_AUTH : HttpURLConnection.HTTP_UNAUTHORIZED; } - String getKeepAlive() { - return "keep-alive"; - } - String getConnection() { + String unauthorizedString() { return authType == HttpAuthType.PROXY - ? "Proxy-Connection" : "Connection"; + ? "Proxy Authentication Required" + : "Unauthorized"; + } + String type() { return type;} + abstract String generateAuthenticateChallenge(); + abstract boolean isAuthentified(String method, Iterator authValues); + } + + private static final class BasicAuthResponder extends AuthResponder { + BasicAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "Basic"); + } + + @Override + String generateAuthenticateChallenge() { + return "Basic realm=\"" + authenticator.getRealm() + "\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Basic".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + + scheme +"'"); + return false; + } + a = a.substring(sp+1); + return validate(a); + } + return false; + } + + boolean validate(String a) { + byte[] b = Base64.getDecoder().decode(a); + String userpass = new String (b); + int colon = userpass.indexOf (':'); + String uname = userpass.substring (0, colon); + String pass = userpass.substring (colon+1); + return authenticator.getUserName().equals(uname) && + new String(authenticator.getPassword(uname)).equals(pass); + } + + } + + private static final class DigestAuthResponder extends AuthResponder { + // This is a very basic DIGEST - used only for the purpose of testing + // the client implementation. Therefore we can get away with never + // updating the server nonce as it makes the implementation of the + // server side digest simpler. + private final byte[] nonce; + private final String ns; + private final String algorithm; + DigestAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType, String algorithm) { + super(authenticator, authType, "Digest"); + nonce = new byte[16]; + new Random(Instant.now().toEpochMilli()).nextBytes(nonce); + ns = new BigInteger(1, nonce).toString(16); + this.algorithm = (algorithm == null) ? "MD5" : algorithm; + } + + @Override + String generateAuthenticateChallenge() { + return "Digest realm=\"" + authenticator.getRealm() + "\"," + + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " + + "\r\n nonce=\"" + ns +"\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Digest".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + scheme +"'"); + return false; + } + a = a.substring(sp+1); + DigestResponse dgr = DigestResponse.create(a); + return validate(method, dgr); + } + return false; + } + + boolean validate(String reqMethod, DigestResponse dg) { + if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { + System.out.println(type + ": Unsupported algorithm " + + dg.algorithm); + return false; + } + if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { + System.out.println(type + ": Unsupported qop " + + dg.qop); + return false; + } + try { + if (!dg.nonce.equals(ns)) { + System.out.println(type + ": bad nonce returned by client: " + + nonce + " expected " + ns); + return false; + } + if (dg.response == null) { + System.out.println(type + ": missing digest response."); + return false; + } + char[] pa = authenticator.getPassword(dg.username); + return verify(reqMethod, dg, pa); + } catch(IllegalArgumentException | SecurityException + | NoSuchAlgorithmException e) { + System.out.println(type + ": " + e.getMessage()); + return false; + } + } + + boolean verify(String reqMethod, DigestResponse dg, char[] pw) + throws NoSuchAlgorithmException { + String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); + if (!dg.response.equals(response)) { + System.out.println(type + ": bad response returned by client: " + + dg.response + " expected " + response); + return false; + } else { + System.out.println(type + ": verified response " + response); + } + return true; + } + + } + + private static final class NoAuthResponder extends AuthResponder { + NoAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "NoAuth"); + } + + @Override + String generateAuthenticateChallenge() { + throw new InternalError("Should not reach here"); + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + return true; + } + } + + private static AuthResponder createAuthResponder(HttpSchemeType schemeType, + HttpTestAuthenticator authenticator, + HttpAuthType authType, + String algorithm) { + switch (schemeType) { + case BASIC, BASICSERVER: return new BasicAuthResponder(authenticator, authType); + case DIGEST: return new DigestAuthResponder(authenticator, authType, algorithm); + case NONE: return new NoAuthResponder(authenticator, authType); + default: throw new IllegalArgumentException( + "Unknown authentication scheme: " + schemeType); + } + } + + // Abstract HTTP filter class. + private abstract static class AbstractHttpFilter extends Filter { + + final AuthResponder authResponder; + final String type; + public AbstractHttpFilter(AuthResponder authResponder) { + this.authResponder = authResponder; + this.type = authResponder.type(); + } + + final String getAuthenticate() { + return authResponder.authenticateHeader(); + } + final String getAuthorization() { + return authResponder.authorizationHeader(); + } + final int getUnauthorizedCode() { + return authResponder.unauthorizedCode(); } protected abstract boolean isAuthentified(HttpExchange he) throws IOException; protected abstract void requestAuthentication(HttpExchange he) throws IOException; @@ -694,11 +896,10 @@ public static DigestResponse create(String raw) { } - private class HttpNoAuthFilter extends AbstractHttpFilter { + private static final class HttpNoAuthFilter extends AbstractHttpFilter { - public HttpNoAuthFilter(HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "NoAuth Server" : "NoAuth Proxy"); + public HttpNoAuthFilter(AuthResponder authResponder) { + super(authResponder); } @Override @@ -720,19 +921,15 @@ public String description() { // An HTTP Filter that performs Basic authentication private class HttpBasicFilter extends AbstractHttpFilter { - - private final HttpTestAuthenticator auth; - public HttpBasicFilter(HttpTestAuthenticator auth, HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "Basic Server" : "Basic Proxy"); - this.auth = auth; + public HttpBasicFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Basic realm=\"" + auth.getRealm() + "\""); + String challenge = authResponder.generateAuthenticateChallenge(); + he.getResponseHeaders().add(getAuthenticate(), challenge); System.out.println(type + ": Requesting Basic Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -742,39 +939,12 @@ protected boolean isAuthentified(HttpExchange he) { if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Basic".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" - + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" - + scheme +"'"); - return false; - } - a = a.substring(sp+1); - return validate(a); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), + authorization.iterator()); } return false; } - boolean validate(String a) { - byte[] b = Base64.getDecoder().decode(a); - String userpass = new String (b); - int colon = userpass.indexOf (':'); - String uname = userpass.substring (0, colon); - String pass = userpass.substring (colon+1); - return auth.getUserName().equals(uname) && - new String(auth.getPassword(uname)).equals(pass); - } - @Override public String description() { return "Filter for " + type; @@ -786,31 +956,14 @@ public String description() { // An HTTP Filter that performs Digest authentication private class HttpDigestFilter extends AbstractHttpFilter { - // This is a very basic DIGEST - used only for the purpose of testing - // the client implementation. Therefore we can get away with never - // updating the server nonce as it makes the implementation of the - // server side digest simpler. - private final HttpTestAuthenticator auth; - private final byte[] nonce; - private final String ns; - private final String algorithm; - public HttpDigestFilter(HttpTestAuthenticator auth, HttpAuthType authType, String algorithm) { - super(authType, authType == HttpAuthType.SERVER - ? "Digest Server" : "Digest Proxy"); - this.auth = auth; - nonce = new byte[16]; - new Random(Instant.now().toEpochMilli()).nextBytes(nonce); - ns = new BigInteger(1, nonce).toString(16); - this.algorithm = (algorithm == null) ? "MD5" : algorithm; + public HttpDigestFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Digest realm=\"" + auth.getRealm() + "\"," - + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " - + "\r\n nonce=\"" + ns +"\""); + he.getResponseHeaders().add(getAuthenticate(), authResponder.generateAuthenticateChallenge()); System.out.println(type + ": Requesting Digest Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -819,71 +972,11 @@ protected void requestAuthentication(HttpExchange he) protected boolean isAuthentified(HttpExchange he) { if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Digest".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" + scheme +"'"); - return false; - } - a = a.substring(sp+1); - DigestResponse dgr = DigestResponse.create(a); - return validate(he.getRequestMethod(), dgr); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), authorization.iterator()); } return false; } - boolean validate(String reqMethod, DigestResponse dg) { - if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { - System.out.println(type + ": Unsupported algorithm " - + dg.algorithm); - return false; - } - if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { - System.out.println(type + ": Unsupported qop " - + dg.qop); - return false; - } - try { - if (!dg.nonce.equals(ns)) { - System.out.println(type + ": bad nonce returned by client: " - + nonce + " expected " + ns); - return false; - } - if (dg.response == null) { - System.out.println(type + ": missing digest response."); - return false; - } - char[] pa = auth.getPassword(dg.username); - return verify(reqMethod, dg, pa); - } catch(IllegalArgumentException | SecurityException - | NoSuchAlgorithmException e) { - System.out.println(type + ": " + e.getMessage()); - return false; - } - } - - boolean verify(String reqMethod, DigestResponse dg, char[] pw) - throws NoSuchAlgorithmException { - String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); - if (!dg.response.equals(response)) { - System.out.println(type + ": bad response returned by client: " - + dg.response + " expected " + response); - return false; - } else { - System.out.println(type + ": verified response " + response); - } - return true; - } - @Override public String description() { return "Filter for DIGEST authentication"; @@ -979,22 +1072,23 @@ public void configure (HttpsParameters params) { } } - // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden - // behind a fake proxy that only understands CONNECT requests. - // The fake proxy is just a server socket that intercept the - // CONNECT and then redirect streams to the real server. + // The HttpsProxyTunnel is a proxy that only understands + // CONNECT requests. It is only used for tunnelling, but + // supports Proxy Authentication with the help of an + // AuthResponder static class HttpsProxyTunnel extends HTTPTestServer implements Runnable { final ServerSocket ss; + final AuthResponder authResponder; private volatile boolean stop; public HttpsProxyTunnel(HttpServer server, HTTPTestServer target, - HttpHandler delegate) + HttpHandler delegate, AuthResponder authResponder) throws IOException { super(server, target, delegate); System.out.flush(); - System.err.println("WARNING: HttpsProxyTunnel is an experimental test class"); + this.authResponder = authResponder; ss = ServerSocketFactory.create(); start(); } @@ -1048,28 +1142,6 @@ public InetSocketAddress getProxyAddress() { return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort()); } - // This is a bit shaky. It doesn't handle continuation - // lines, but our client shouldn't send any. - // Read a line from the input stream, swallowing the final - // \r\n sequence. Stops at the first \n, doesn't complain - // if it wasn't preceded by '\r'. - // - String readLine(InputStream r) throws IOException { - StringBuilder b = new StringBuilder(); - int c; - while ((c = r.read()) != -1) { - if (c == '\n') break; - b.appendCodePoint(c); - } - if (b.length() == 0) { - return ""; - } - if (b.codePointAt(b.length() -1) == '\r') { - b.delete(b.length() -1, b.length()); - } - return b.toString(); - } - @Override public void run() { Socket clientConnection = null; @@ -1137,6 +1209,37 @@ public void run() { } } + private boolean isAuthentified(MessageHeader request) { + String requestLine = request.getValue(0); + String method = requestLine.substring(0, requestLine.indexOf(' ')); + assert "CONNECT".equals(method); + return authResponder.isAuthentified(method, + request.multiValueIterator(authResponder.authorizationHeader())); + } + + private String challengeResponse() { + return "HTTP/1.1 " + authResponder.unauthorizedCode() + " " + + authResponder.unauthorizedString() + + "\r\nContent-Length: 0\r\n" + + authResponder.authenticateHeader() + ": " + + authResponder.generateAuthenticateChallenge() + + "\r\n\r\n"; + } + + private String okResponse() { + return "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + } + + private String badGatewayResponse() { + return "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n"; + } + + private void sendResponse(PrintWriter pw, String response) { + System.out.println("Tunnel: Sending " + response); + pw.print(response); + pw.flush(); + } + private void processRequestAndWaitToComplete(final Socket clientConnection) throws IOException, InterruptedException { final Socket targetConnection; @@ -1146,32 +1249,24 @@ private void processRequestAndWaitToComplete(final Socket clientConnection) clientConnection.getOutputStream(), "UTF-8"); PrintWriter pw = new PrintWriter(w); System.out.println("Tunnel: Reading request line"); - String requestLine = readLine(ccis); + MessageHeader request = new MessageHeader(ccis); + String requestLine = request.getValue(0); System.out.println("Tunnel: Request line: " + requestLine); - if (requestLine.startsWith("CONNECT ")) { - // We should probably check that the next word following - // CONNECT is the host:port of our HTTPS serverImpl. - // Some improvement for a followup! - - // Read all headers until we find the empty line that - // signals the end of all headers. - while(!requestLine.equals("")) { - System.out.println("Tunnel: Reading header: " - + (requestLine = readLine(ccis))); + if (requestLine != null && requestLine.startsWith("CONNECT ")) { + if (!isAuthentified(request)) { + sendResponse(pw, challengeResponse()); + return; } - targetConnection = new Socket( serverImpl.getAddress().getAddress(), serverImpl.getAddress().getPort()); // Then send the 200 OK response to the client - System.out.println("Tunnel: Sending " - + "HTTP/1.1 200 OK\r\n\r\n"); - pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); - pw.flush(); + sendResponse(pw, okResponse()); } else { // This should not happen. If it does then consider it a // client error and throw an IOException + sendResponse(pw, badGatewayResponse()); System.out.println("Tunnel: Throwing an IOException due to unexpected" + " request line: " + requestLine); throw new IOException("Client request error - Unexpected request line"); diff --git a/test/jdk/java/nio/file/Files/CopyAndMove.java b/test/jdk/java/nio/file/Files/CopyAndMove.java index f8bc9f997b75..ca4cc0aaf00c 100644 --- a/test/jdk/java/nio/file/Files/CopyAndMove.java +++ b/test/jdk/java/nio/file/Files/CopyAndMove.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, 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 @@ -138,10 +138,10 @@ static void checkBasicAttributes(BasicFileAttributes attrs1, BasicFileAttributes attrs2) { // check file type - assertTrue(attrs1.isRegularFile() == attrs2.isRegularFile()); - assertTrue(attrs1.isDirectory() == attrs2.isDirectory()); - assertTrue(attrs1.isSymbolicLink() == attrs2.isSymbolicLink()); - assertTrue(attrs1.isOther() == attrs2.isOther()); + assertEquals(attrs1.isRegularFile(), attrs2.isRegularFile()); + assertEquals(attrs1.isDirectory(), attrs2.isDirectory()); + assertEquals(attrs1.isSymbolicLink(), attrs2.isSymbolicLink()); + assertEquals(attrs1.isOther(), attrs2.isOther()); // check last modified time if not a symbolic link if (!attrs1.isSymbolicLink()) { @@ -162,30 +162,43 @@ static void checkBasicAttributes(BasicFileAttributes attrs1, // check size if (attrs1.isRegularFile()) - assertTrue(attrs1.size() == attrs2.size()); + assertEquals(attrs1.size(), attrs2.size()); + } + + static void setWindowsLinkAttributes(Path link) throws IOException { + // Set the timestamp to 2000-01-01T00:00:00Z. + FileTime time = FileTime.from(946684800, TimeUnit.SECONDS); + + BasicFileAttributeView basicView = + getFileAttributeView(link, BasicFileAttributeView.class, NOFOLLOW_LINKS); + basicView.setTimes(/* mtime */ time, /* atime */ null, /* creation */ time); + + DosFileAttributeView dosView = + getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS); + dosView.setHidden(true); } static void checkPosixAttributes(PosixFileAttributes attrs1, PosixFileAttributes attrs2) { - assertTrue(attrs1.permissions().equals(attrs2.permissions()), + assertEquals(attrs1.permissions(), attrs2.permissions(), "permissions%n1 (%d): %s%n2 (%d): %s%n%n", attrs1.permissions().size(), attrs1.permissions(), attrs2.permissions().size(), attrs2.permissions()); - assertTrue(attrs1.owner().equals(attrs2.owner()), + assertEquals(attrs1.owner(), attrs2.owner(), "owner%n1: %s%n2: %s%n%n", attrs1.owner(), attrs2.owner()); - assertTrue(attrs1.group().equals(attrs2.group()), + assertEquals(attrs1.group(), attrs2.group(), "group%n1: %s%n2: %s%n%n", attrs1.group(), attrs2.group()); } static void checkDosAttributes(DosFileAttributes attrs1, DosFileAttributes attrs2) { - assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly(), + assertEquals(attrs1.isReadOnly(), attrs2.isReadOnly(), "isReadOnly%n1: %s%n2: %s%n%n", attrs1.isReadOnly(), attrs2.isReadOnly()); - assertTrue(attrs1.isHidden() == attrs2.isHidden(), + assertEquals(attrs1.isHidden(), attrs2.isHidden(), "isHidden%n1: %s%n2: %s%n%n", attrs1.isHidden(), attrs2.isHidden()); - assertTrue(attrs1.isSystem() == attrs2.isSystem(), + assertEquals(attrs1.isSystem(), attrs2.isSystem(), "isSystem%n1: %s%n2: %s%n%n", attrs1.isSystem(), attrs2.isSystem()); } @@ -197,7 +210,7 @@ static void checkUserDefinedFileAttributes(Map attrs1, ByteBuffer bb1 = attrs1.get(name); ByteBuffer bb2 = attrs2.get(name); assertTrue(bb2 != null); - assertTrue(bb1.equals(bb2)); + assertEquals(bb1, bb2); } } @@ -211,7 +224,7 @@ static Map readUserDefinedFileAttributes(Path file) int size = view.size(name); ByteBuffer bb = ByteBuffer.allocate(size); int n = view.read(name, bb); - assertTrue(n == size); + assertEquals(n, size); bb.flip(); result.put(name, bb); } @@ -256,7 +269,7 @@ static void moveAndVerify(Path source, Path target, CopyOption... options) // move file Path result = move(source, target, options); - assertTrue(result == target); + assertEquals(result, target); // verify source does not exist assertTrue(notExists(source)); @@ -677,12 +690,74 @@ static void testMove(Path dir1, Path dir2, boolean supportsSymbolicLinks) delete(source); } + static void checkTargetAttributes(Path source, Path target, + BasicFileAttributes sourceAttrs) + throws IOException + { + checkBasicAttributes(sourceAttrs, + readAttributes(target, BasicFileAttributes.class)); + + // check POSIX attributes are copied + if (!Platform.isWindows() && testPosixAttributes) { + checkPosixAttributes( + readAttributes(source, PosixFileAttributes.class), + readAttributes(target, PosixFileAttributes.class)); + } + + // verify other attributes when same provider + if (source.getFileSystem().provider() == target.getFileSystem().provider()) { + // check DOS attributes are copied + if (Platform.isWindows()) { + checkDosAttributes( + readAttributes(source, DosFileAttributes.class), + readAttributes(target, DosFileAttributes.class)); + } + + // check named attributes are copied + if (getFileStore(source).supportsFileAttributeView("xattr") && + getFileStore(target).supportsFileAttributeView("xattr")) + { + checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source), + readUserDefinedFileAttributes(target)); + } + } + } + + static void checkSymLinkAttributes(Path source, Path target, + BasicFileAttributes sourceAttrs) + throws IOException + { + assertTrue(sourceAttrs.isSymbolicLink()); + + BasicFileAttributes targetAttrs = + readAttributes(target, BasicFileAttributes.class, NOFOLLOW_LINKS); + checkBasicAttributes(sourceAttrs, targetAttrs); + + // verify other attributes when same provider and on Windows + if (Platform.isWindows() && source.getFileSystem().provider() == + target.getFileSystem().provider()) { + // check that timestamps on the source are retained for the target + FileTime srcCreationTime = sourceAttrs.creationTime(); + FileTime tgtCreationTime = targetAttrs.creationTime(); + assertEquals(srcCreationTime, tgtCreationTime); + + FileTime srcModTime = sourceAttrs.lastModifiedTime(); + FileTime tgtModTime = targetAttrs.lastModifiedTime(); + assertEquals(srcModTime, tgtModTime); + + // check DOS attributes are copied + checkDosAttributes( + readAttributes(source, DosFileAttributes.class, NOFOLLOW_LINKS), + readAttributes(target, DosFileAttributes.class, NOFOLLOW_LINKS)); + } + } + // copy source to target with verification static void copyAndVerify(Path source, Path target, CopyOption... options) throws IOException { Path result = copy(source, target, options); - assertTrue(result == target); + assertEquals(result, target); // get attributes of source and target file to verify copy boolean followLinks = true; @@ -701,41 +776,18 @@ static void copyAndVerify(Path source, Path target, CopyOption... options) // check hash if regular file if (basicAttributes.isRegularFile()) - assertTrue(computeHash(source) == computeHash(target)); + assertEquals(computeHash(source), computeHash(target)); // check link target if symbolic link if (basicAttributes.isSymbolicLink()) - assert(readSymbolicLink(source).equals(readSymbolicLink(target))); + assertEquals(readSymbolicLink(source), readSymbolicLink(target)); // check that attributes are copied - if (copyAttributes && followLinks) { - checkBasicAttributes(basicAttributes, - readAttributes(source, BasicFileAttributes.class, linkOptions)); - - // check POSIX attributes are copied - if (!Platform.isWindows() && testPosixAttributes) { - checkPosixAttributes( - readAttributes(source, PosixFileAttributes.class, linkOptions), - readAttributes(target, PosixFileAttributes.class, linkOptions)); - } - - // verify other attributes when same provider - if (source.getFileSystem().provider() == target.getFileSystem().provider()) { - // check DOS attributes are copied - if (Platform.isWindows()) { - checkDosAttributes( - readAttributes(source, DosFileAttributes.class, linkOptions), - readAttributes(target, DosFileAttributes.class, linkOptions)); - } - - // check named attributes are copied - if (followLinks && - getFileStore(source).supportsFileAttributeView("xattr") && - getFileStore(target).supportsFileAttributeView("xattr")) - { - checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source), - readUserDefinedFileAttributes(target)); - } + if (copyAttributes) { + if (followLinks) { + checkTargetAttributes(source, target, basicAttributes); + } else if (basicAttributes.isSymbolicLink()) { + checkSymLinkAttributes(source, target, basicAttributes); } } } @@ -970,6 +1022,23 @@ static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsSymbolicLin delete(source); } + /** + * Test: Copy link + attributes + */ + if (supportsSymbolicLinks) { + source = createSourceFile(dir1); + link = dir1.resolve("link"); + createSymbolicLink(link, source); + if (Platform.isWindows()) { + setWindowsLinkAttributes(link); + } + + target = getTargetFile(dir2); + copyAndVerify(link, target, NOFOLLOW_LINKS, COPY_ATTRIBUTES); + delete(link); + delete(source); + } + /** * Test: Copy link (to directory) */ @@ -984,6 +1053,24 @@ static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsSymbolicLin delete(source); } + /** + * Test: Copy link to directory + attributes + */ + if (supportsSymbolicLinks) { + source = dir1.resolve("mydir"); + createDirectory(source); + link = dir1.resolve("link"); + createSymbolicLink(link, source); + if (Platform.isWindows()) { + setWindowsLinkAttributes(link); + } + + target = getTargetFile(dir2); + copyAndVerify(link, target, NOFOLLOW_LINKS, COPY_ATTRIBUTES); + delete(link); + delete(source); + } + /** * Test: Copy broken link */ @@ -1121,9 +1208,9 @@ static void testCopyInputStreamToFile(int size) throws IOException { } else { n = copy(in, target); } - assertTrue(in.read() == -1); // EOF - assertTrue(n == size); - assertTrue(size(target) == size); + assertEquals(in.read(), -1); // EOF + assertEquals(n, size); + assertEquals(size(target), size); } finally { in.close(); } @@ -1171,15 +1258,15 @@ static void testCopyFileToOuputStream(int size) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); long n = copy(source, out); - assertTrue(n == size); - assertTrue(out.size() == size); + assertEquals(n, size); + assertEquals(out.size(), size); byte[] read = out.toByteArray(); assertTrue(Arrays.equals(read, b)); // check output stream is open out.write(0); - assertTrue(out.size() == size+1); + assertEquals(out.size(), size+1); } finally { delete(source); } @@ -1197,6 +1284,25 @@ static void assertTrue(boolean value, String format, Object... args) { } } + static void assertEquals(long lhs, long rhs) { + if (lhs != rhs) { + throw new RuntimeException("assertEquals: " + lhs + " != " + rhs); + } + } + + static void assertEquals(Object lhs, Object rhs) { + assertEquals(lhs, rhs, null); + } + + static void assertEquals(Object lhs, Object rhs, String format, Object... args) { + if (!Objects.equals(lhs, rhs)) { + String msg = format == null + ? "assertEquals: " + Objects.toString(lhs) + " != " + Objects.toString(rhs) + : String.format(format, args); + throw new RuntimeException(msg); + } + } + // computes simple hash of the given file static int computeHash(Path file) throws IOException { int h = 0; diff --git a/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java new file mode 100644 index 000000000000..2cb1b9ae32e8 --- /dev/null +++ b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java @@ -0,0 +1,92 @@ +/* + * 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 8234071 8387267 + * @summary AUTO_RESIZE_LAST_COLUMN should resize only the last column during table layout + * @run main TestAutoResizeLastColumn + */ + +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.table.TableColumnModel; + +public class TestAutoResizeLastColumn { + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(TestAutoResizeLastColumn::testLastColumnOnly); + } + + private static void testLastColumnOnly() { + JTable table = new JTable(3, 3); + table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + + if (table.getTableHeader().getResizingColumn() != null) { + throw new RuntimeException( + "AUTO_RESIZE_LAST_COLUMN must not set resizingColumn"); + } + + TableColumnModel cm = table.getColumnModel(); + for (int i = 0; i < cm.getColumnCount(); i++) { + cm.getColumn(i).setMinWidth(10); + cm.getColumn(i).setPreferredWidth(100); + cm.getColumn(i).setWidth(100); + } + + table.setSize(300, 100); + table.doLayout(); + + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 100); + + table.setSize(360, 100); + table.doLayout(); + + /* + * AUTO_RESIZE_LAST_COLUMN means the +60 delta is absorbed by + * the last column only. + * Without fix all columns width will change to 120. + */ + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 160); + + table.setSize(330, 100); + table.doLayout(); + + assertWidth(cm, 0, 100); + assertWidth(cm, 1, 100); + assertWidth(cm, 2, 130); + } + + private static void assertWidth(TableColumnModel cm, int column, int expected) { + int actual = cm.getColumn(column).getWidth(); + if (actual != expected) { + throw new RuntimeException( + "Unexpected width for column " + column + + ": expected " + expected + + ", actual " + actual); + } + } +} diff --git a/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java b/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java new file mode 100644 index 000000000000..3e1669cb9270 --- /dev/null +++ b/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java @@ -0,0 +1,91 @@ +/* + * 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 8390296 + * @summary Verifies MultiUIDefaults#containsKey and + * MultiUIDefaults#keySet().contains(key) produce same result + * @run main MultiUIDefaultsContainsKeyTest + */ + +import javax.swing.UIManager; +import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; + +public class MultiUIDefaultsContainsKeyTest { + private static final String KEY = "TabbedPane.isTabRollover"; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(new KeyProvidingLookAndFeel()); + + boolean containsKey = UIManager.getDefaults().containsKey(KEY); + boolean keySetContains = UIManager.getDefaults().keySet().contains(KEY); + + System.out.println("key: " + KEY); + System.out.println("defaults class: " + + UIManager.getDefaults().getClass().getName()); + System.out.println("containsKey: " + containsKey); + System.out.println("keySet().contains: " + keySetContains); + + if (containsKey != keySetContains) { + throw new RuntimeException("containsKey and keySet().contains disagree"); + } + + System.out.println("No inconsistency observed."); + } + + private static final class KeyProvidingLookAndFeel extends LookAndFeel { + @Override + public String getName() { + return "auxiliary Look and Feel"; + } + + @Override + public String getID() { + return "Auxiliary"; + } + + @Override + public String getDescription() { + return "Adds a defaults key for the MultiUIDefaults reproducer"; + } + + @Override + public boolean isNativeLookAndFeel() { + return false; + } + + @Override + public boolean isSupportedLookAndFeel() { + return true; + } + + @Override + public UIDefaults getDefaults() { + UIDefaults defaults = new UIDefaults(); + defaults.put(KEY, Boolean.TRUE); + return defaults; + } + } +} diff --git a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java index c27a1c7480fb..53924b6abadb 100644 --- a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java +++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.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 @@ -65,43 +65,58 @@ private static void testMemoryLimit(String value) { } private static void testMemoryFailCount() { - long memAndSwapLimit = Metrics.systemMetrics().getMemoryAndSwapLimit(); - long memLimit = Metrics.systemMetrics().getMemoryLimit(); + final Metrics metrics = Metrics.systemMetrics(); + final long memAndSwapLimit = metrics.getMemoryAndSwapLimit(); + final long memLimit = metrics.getMemoryLimit(); + + final int M = 1024 * 1024; + + // We need swap to execute this test. Otherwise OOM killer acts with + // SIGKILL before we read the fail counter. + + final long maxHeapSize = Runtime.getRuntime().maxMemory(); + if (maxHeapSize <= memLimit || maxHeapSize >= memAndSwapLimit) { + throw new RuntimeException( + "Expected memory limit < maximum heap < memory-and-swap limit: " + + "memory=" + memLimit / M + "M, " + + "heap=" + maxHeapSize / M + "M, " + + "memory-and-swap=" + memAndSwapLimit / M + "M"); + } - // We need swap to execute this test or will SEGV - if (memAndSwapLimit <= memLimit) { - System.out.println("No swap memory limits. Ignoring test!"); - } else { - long count = Metrics.systemMetrics().getMemoryFailCount(); - - // Allocate 512M of data in 1M chunks per iteration - byte[][] bytes = new byte[64 * 8][]; - boolean atLeastOneAllocationWorked = false; - for (int i = 0; i < 64 * 8; i++) { - try { - bytes[i] = new byte[1024 * 1024]; - atLeastOneAllocationWorked = true; - // Break out as soon as we see an increase in failcount - // to avoid getting killed by the OOM killer. - if (Metrics.systemMetrics().getMemoryFailCount() > count) { - break; - } - } catch (Error e) { // OOM error - break; - } - } - if (!atLeastOneAllocationWorked) { - System.out.println("Allocation failed immediately. Ignoring test!"); - return; + final long initialFailCount = metrics.getMemoryFailCount(); + + System.out.println("Initial memory fail count: " + initialFailCount); + + // Allocate 512M of data in 1M chunks per iteration + byte[][] bytes = new byte[512][]; + + for (int i = 0; i < 512; i++) { + if (i % 8 == 0) { + System.out.printf("Allocated: %3dM, Memory usage: %3dM, Memory and swap: %3dM\n", + i, + metrics.getMemoryUsage() / M, + metrics.getMemoryAndSwapUsage() / M); + } else { + System.out.print("."); } - // Be sure bytes allocations don't get optimized out - System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length); - if (Metrics.systemMetrics().getMemoryFailCount() <= count) { - throw new RuntimeException("Memory fail count : new : [" - + Metrics.systemMetrics().getMemoryFailCount() + "]" - + ", old : [" + count + "]"); + bytes[i] = new byte[M]; + Arrays.fill(bytes[i], (byte) 1); // dirty every page + // Break out as soon as we see an increase in failcount + if (metrics.getMemoryFailCount() > initialFailCount) { + break; } } + + // Be sure bytes allocations don't get optimized out + System.out.println("\nDEBUG: Bytes allocation length 1: " + bytes[0].length); + final long newCount = metrics.getMemoryFailCount(); + System.out.println("Final memory fail count: " + newCount); + + if (newCount <= initialFailCount) { + throw new RuntimeException("Memory fail count did not increase: initial=" + + initialFailCount + ", final=" + newCount); + } + System.out.println("TEST PASSED!!!"); } diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 12f90d655169..78878879783a 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.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 @@ -76,7 +76,7 @@ public static void main(String[] args) throws Exception { } testOomKillFlag("100m", true); - testMemoryFailCount("128m"); + testMemoryFailCount("128m" /*memory*/, "768m" /*max_heap*/, "1024m" /*memory_n_swap*/); testMemorySoftLimit("500m","200m"); @@ -105,36 +105,47 @@ private static void testMemoryLimit(String value, boolean addCgroupMount) throws DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); } - private static void testMemoryFailCount(String value) throws Exception { - Common.logNewTestCase("testMemoryFailCount" + value); + private static void testMemoryFailCount(String memory, String heap, String memoryAndSwap) throws Exception { + Common.logNewTestCase("testMemoryFailCount, memory = " + memory + + ", heap = " + heap + + ", memory + swap = " + memoryAndSwap); // Check whether swapping really works for this test // On some systems there is no swap space enabled. And running - // 'java -Xms{mem-limit} -Xmx{mem-limit} -XX:+AlwaysPreTouch -version' + // 'java -Xms{heap} -Xmx{heap} -XX:+AlwaysPreTouch -version' // would fail due to swap space size being 0. Note that when swap is - // properly enabled on the system the container gets the same amount - // of swap as is configured for memory. Thus, 2x{mem-limit} is the actual - // memory and swap bound for this pre-test. + // properly enabled, the explicit memory-and-swap limit gives the JVM + // enough headroom to exceed the physical memory limit without being + // killed by the OOM killer. DockerRunOptions preOpts = new DockerRunOptions(imageName, "/jdk/bin/java", "-version"); preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") - .addDockerOpts("--memory=" + value) + .addDockerOpts("--memory=" + memory) + .addDockerOpts("--memory-swap=" + memoryAndSwap) .addJavaOpts("-XX:+AlwaysPreTouch") - .addJavaOpts("-Xms" + value) - .addJavaOpts("-Xmx" + value); + .addJavaOptsAppended("-XX:InitialHeapSize=" + heap) + .addJavaOptsAppended("-XX:MaxHeapSize=" + heap); OutputAnalyzer oa = DockerTestUtils.dockerRunJava(preOpts); String output = oa.getOutput(); if (!output.contains("version")) { throw new SkippedException("Swapping doesn't work for this test."); } + // 0 128 1024 + // |---o----------------|---------------------------X--------------)-------------| + // START memory.max growth target MaxHeapSize memory+swap limit + // o~~~~~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~> (growth) OOM + // failcount: 0 1 2 3 . . . N + // DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester"); opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/") - .addDockerOpts("--memory=" + value) - .addJavaOpts("-Xmx" + value) + .addDockerOpts("--memory=" + memory) + .addDockerOpts("--memory-swap=" + memoryAndSwap) .addJavaOpts("-cp", "/test-classes/") .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") + // set the required heap size *after* inherited jtreg options + .addJavaOptsAppended("-XX:MaxHeapSize=" + heap) .addClassOptions("failcount"); oa = DockerTestUtils.dockerRunJava(opts); output = oa.getOutput(); diff --git a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java b/test/jdk/jdk/internal/vm/Continuation/Fuzz.java index 71038b003e31..25b5601188f0 100644 --- a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java +++ b/test/jdk/jdk/internal/vm/Continuation/Fuzz.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 @@ -58,6 +58,25 @@ * Fuzz */ +/* + * @test id=verify-cont + * @key randomness + * @summary Fuzz tests for jdk.internal.vm.Continuation + * @requires vm.debug == true & vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base java.base/jdk.internal.vm.annotation java.base/jdk.internal.vm + * @library /test/lib + * @enablePreview + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm/timeout=1200 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+VerifyContinuations + * Fuzz + */ + import jdk.internal.vm.Continuation; import jdk.internal.vm.ContinuationScope; diff --git a/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java new file mode 100644 index 000000000000..d1a2b65728a1 --- /dev/null +++ b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java @@ -0,0 +1,45 @@ +/* + * 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 8378071 + * @summary Test jdk.internal.vm.ThreadSnapshot.of(Thread) correctly initialize ThreadLock class + * + * @modules java.base/jdk.internal.vm + * @run main ThreadLockClassInit + * @run main/othervm -Xcomp -XX:-Inline -XX:CompileCommand=compileonly,*ThreadSnapshot*::* ThreadLockClassInit + */ + +import jdk.internal.vm.ThreadSnapshot; + +public class ThreadLockClassInit { + public static final Object LOCK = new Object(); + + public static void main(String[] args) throws Exception { + synchronized (LOCK) { + // The ThreadSnapshot doesn't have any public methods so nothing to check. + ThreadSnapshot.of(Thread.currentThread()); + } + } +} diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index e4e0d5f3bfd7..7c8cb9b4b556 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -477,7 +477,7 @@ protected String vmCDSSupportsAOTClassLinking() { protected String vmCDSSupportsAOTCodeCaching() { if ("true".equals(vmCDSSupportsAOTClassLinking()) && !"zero".equals(vmFlavor()) && - (Platform.isX64() || Platform.isAArch64())) { + (Platform.isX64() || Platform.isAArch64() || Platform.isRISCV64())) { return "true"; } else { return "false"; diff --git a/test/langtools/tools/javac/SuperInit/InitAssignOp.java b/test/langtools/tools/javac/SuperInit/InitAssignOp.java new file mode 100644 index 000000000000..499a2ba32503 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/InitAssignOp.java @@ -0,0 +1,292 @@ +/* + * 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 8389659 + * @summary Verify assign op is handled correctly in the constructor prologue + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import toolbox.JavaTask; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class InitAssignOp { + + private final ToolBox tb = new ToolBox(); + private Path base; + + @Test + void testInitAssignOp() throws Exception { + record TestCase(String source, List expectedCompilationOutput) {} + TestCase[] tests = new TestCase[] { + new TestCase(""" + public class Test { + private int i; + + public Test() { + ++this.i; + super(); + } + public static void main(String... args) { + System.out.println(new Test().i); + } + } + """, + List.of( + "Test.java:5:15: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)", + "1 error" + )), + new TestCase(""" + public class Test { + private int i; + + public Test() { + this.i++; + super(); + } + public static void main(String... args) { + System.out.println(new Test().i); + } + } + """, + List.of( + "Test.java:5:13: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)", + "1 error" + )), + new TestCase(""" + public class Test { + private int i; + + public Test() { + this.i += 1; + super(); + } + public static void main(String... args) { + System.out.println(new Test().i); + } + } + """, + List.of( + "Test.java:5:13: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)", + "1 error" + )), + }; + for (TestCase test : tests) { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out; + + out = + new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .sources(test.source()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + Assertions.assertEquals(test.expectedCompilationOutput(), out); + new JavacTask(tb) + .options("--enable-preview", "--release", System.getProperty("java.specification.version")) + .outdir(classes) + .sources(test.source()) + .run() + .writeAll(); + + out = + new JavaTask(tb) + .vmOptions("--enable-preview") + .classpath(classes.toString()) + .className("Test") + .run() + .getOutputLines(Task.OutputKind.STDOUT); + List expectedRunOutput = List.of( + "1" + ); + Assertions.assertEquals(expectedRunOutput, out); + } + } + + @Test + void testInitAssignOpErrors() throws Exception { + record TestCase(String source, List expectedCompilationOutput) {} + TestCase[] tests = new TestCase[] { + new TestCase(""" + public class Test { + public Test() { + ++0; + super(); + } + } + """, + List.of( + "Test.java:3:11: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test() { + ++test(); + super(); + } + private static int test() { return 1; } + } + """, + List.of( + "Test.java:3:15: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test(int i) { + ++(i + 1); + super(); + } + } + """, + List.of( + "Test.java:3:14: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + + new TestCase(""" + public class Test { + public Test() { + 0++; + super(); + } + } + """, + List.of( + "Test.java:3:9: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test() { + test()++; + super(); + } + private static int test() { return 1; } + } + """, + List.of( + "Test.java:3:13: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test(int i) { + (i + 1)++; + super(); + } + } + """, + List.of( + "Test.java:3:12: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + + new TestCase(""" + public class Test { + public Test() { + 0 += 1; + super(); + } + } + """, + List.of( + "Test.java:3:9: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test() { + test() += 1; + super(); + } + private static int test() { return 1; } + } + """, + List.of( + "Test.java:3:13: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + new TestCase(""" + public class Test { + public Test(int i) { + (i + 1) += 1; + super(); + } + } + """, + List.of( + "Test.java:3:12: compiler.err.unexpected.type: kindname.variable, kindname.value", + "1 error" + )), + }; + for (TestCase test : tests) { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out; + + out = + new JavacTask(tb) + .options("-XDrawDiagnostics", + "--enable-preview", "--release", System.getProperty("java.specification.version")) + .outdir(classes) + .sources(test.source()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + Assertions.assertEquals(test.expectedCompilationOutput(), out); + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } + +} diff --git a/test/langtools/tools/javac/attr/Synchronized.java b/test/langtools/tools/javac/attr/Synchronized.java new file mode 100644 index 000000000000..783a709dbd2f --- /dev/null +++ b/test/langtools/tools/javac/attr/Synchronized.java @@ -0,0 +1,148 @@ +/* + * 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 8389987 + * @summary Verify correct handling of synchronized + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit Synchronized +*/ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class Synchronized { + + private Path base; + private ToolBox tb = new ToolBox(); + + @Test + public void testSynchronizedNull() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + public class Test { + void t() { + synchronized (null) {} + } + } + """); + + Files.createDirectories(classes); + + List log; + List expected; + + log = new JavacTask(tb) + .options("-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + expected = List.of( + "Test.java:3:9: compiler.err.type.found.req: compiler.misc.type.null, (compiler.misc.type.req.ref)", + "1 error" + ); + + assertEquals(expected, log); + + log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "--enable-preview", "--release", System.getProperty("java.specification.version")) + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + expected = List.of( + "Test.java:3:9: compiler.err.type.found.req: compiler.misc.type.null, (compiler.misc.type.req.identity)", + "1 error" + ); + + assertEquals(expected, log); + } + + @Test + public void testSynchronizedValueClassValueClassesDisabled() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + public value class Test { + void t() { + synchronized (new Test()) {} + } + } + """); + + Files.createDirectories(classes); + + List log; + List expected; + + log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-XDshould-stop.at=WARN") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + expected = List.of( + "Test.java:1:8: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)", + "Test.java:3:9: compiler.err.type.found.req: Test, (compiler.misc.type.req.ref)", + "2 errors" + ); + + assertEquals(expected, log); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/classfiles/attributes/LocalVariableTable/EmptyLocalVariableTableTest.java b/test/langtools/tools/javac/classfiles/attributes/LocalVariableTable/EmptyLocalVariableTableTest.java new file mode 100644 index 000000000000..1f677208d6b6 --- /dev/null +++ b/test/langtools/tools/javac/classfiles/attributes/LocalVariableTable/EmptyLocalVariableTableTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8389868 + * @summary Test that empty LocalVariableTable attribute is not generated. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.attribute.CodeAttribute; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class EmptyLocalVariableTableTest { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testNoEmptyLVT() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-g") + .sources(""" + public class UnusedVariable { + static void test() { + { + Class unused = null; + } + } + static { + Class unused = null; + } + } + """) + .run() + .writeAll(); + ClassFile.of().parse(classes.resolve("UnusedVariable.class")).methods().stream() + .forEach(mm -> { + CodeAttribute codeAttribute = mm.findAttribute(Attributes.code()).orElse(null); + Assertions.assertNotNull(codeAttribute); + codeAttribute.findAttributes(Attributes.localVariableTable()).stream() + .forEach(attr -> { + Assertions.assertFalse(attr.localVariables().isEmpty(), "Empty LocalVariableTableAttribute found"); + }); + }); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javac/generics/inference/T8389071.java b/test/langtools/tools/javac/generics/inference/T8389071.java new file mode 100644 index 000000000000..a0c2fcd1b71c --- /dev/null +++ b/test/langtools/tools/javac/generics/inference/T8389071.java @@ -0,0 +1,61 @@ +/* + * 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 8389071 + * @summary javac OutOfMemoryError when computing lub for types with self-referential + * witness-typed interfaces + * @compile T8389071.java + */ + +import java.util.Arrays; + +class T8389071 { + private interface I> {} + private interface J> {} + private interface K> {} + private interface L> {} + private interface M> {} + private interface N> {} + + private static class ConsStruct { + private static class Empty extends ConsStruct {} + private static class Cons extends ConsStruct {} + } + + private static class A6 extends ConsStruct.Cons implements + I>, J>, K>, L>, M>, N> {} + private static class B6 extends ConsStruct.Cons> implements + I>, J>, K>, L>, M>, N> {} + private static class C6 extends ConsStruct.Cons> implements + I>, J>, K>, L>, M>, N> {} + + void foo() { + A6 a = new A6<>(); + B6 b = new B6<>(); + C6 c = new C6<>(); + java.util.List> list = + Arrays.asList(a, b, c); + } +} diff --git a/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java new file mode 100644 index 000000000000..4b8485f89297 --- /dev/null +++ b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java @@ -0,0 +1,9 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8388979 + * @summary javac crashes with NullPointerException in Types.erasure when evaluating bounds + * of mutually dependent array type variables inside an intersection type definition + * @compile/fail/ref=NPEArrayInIntersectionTest.out -XDrawDiagnostics NPEArrayInIntersectionTest.java + */ + +class NPEArrayInIntersectionTest {} diff --git a/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out new file mode 100644 index 000000000000..3888a8743705 --- /dev/null +++ b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out @@ -0,0 +1,2 @@ +NPEArrayInIntersectionTest.java:9:45: compiler.err.type.found.req: B[], (compiler.misc.type.req.class) +1 error diff --git a/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod new file mode 100644 index 000000000000..fb558cb42305 --- /dev/null +++ b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod @@ -0,0 +1,74 @@ +/* A hand-crafted classfile carrying a generic ClassSignature attribute of + * the form "Ljava/lang/Object;" + * i.e. the classfile-level equivalent of: + * + * class TypeVarArrayInBound {} + */ +class TypeVarArrayInBound { + 0xCAFEBABE; + 0; // minor version + 72; // version + [] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 + class #4; // #2 + NameAndType #5 #6; // #3 + Utf8 "java/lang/Object"; // #4 + Utf8 ""; // #5 + Utf8 "()V"; // #6 + class #8; // #7 + Utf8 "TypeVarArrayInBound"; // #8 + Utf8 "Code"; // #9 + Utf8 "LineNumberTable"; // #10 + Utf8 "Signature"; // #11 + Utf8 "Ljava/lang/Object;"; // #12 + Utf8 "SourceFile"; // #13 + Utf8 "TypeVarArrayInBound.java"; // #14 + } // Constant Pool + + 0x0020; // access + #7;// this_cpx + #2;// super_cpx + + [] { // Interfaces + } // Interfaces + + [] { // Fields + } // Fields + + [] { // Methods + { // method + 0x0000; // access + #5; // name_index + #6; // descriptor_index + [] { // Attributes + Attr(#9) { // Code + 1; // max_stack + 1; // max_locals + Bytes[]{ + 0x2AB70001B1; + } + [] { // Traps + } // end Traps + [] { // Attributes + Attr(#10) { // LineNumberTable + [] { // line_number_table + 0 1; + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } + } // Methods + + [] { // Attributes + Attr(#11) { // Signature + #12; + } // end Signature + ; + Attr(#13) { // SourceFile + #14; + } // end SourceFile + } // Attributes +} // end class TypeVarArrayInBound diff --git a/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java new file mode 100644 index 000000000000..5a5134a72713 --- /dev/null +++ b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java @@ -0,0 +1,12 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8388979 + * @summary javac crashes with NullPointerException in Types.erasure when + * evaluating bounds of mutually dependent array type variables + * inside an intersection type definition + * @build TypeVarArrayInBound + * @compile TypeVarArrayInBoundTest.java + */ +class TypeVarArrayInBoundTest { + TypeVarArrayInBound field; +} diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java index f94ba461a450..9b4ff972ba74 100644 --- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java +++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8013852 8031744 8225377 8323684 + * @bug 8013852 8031744 8225377 8323684 8389859 * @summary Annotations on types * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -103,7 +103,9 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor { new NameToAnnotationEntry("BasicAnnoTests.TB", BasicAnnoTests.TB.class), new NameToAnnotationEntry("BasicAnnoTests.TC", BasicAnnoTests.TC.class), new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class), - new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class)); + new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class), + new NameToAnnotationEntry("BasicAnnoTests.DTF", BasicAnnoTests.DTF.class), + new NameToAnnotationEntry("BasicAnnoTests.DTP", BasicAnnoTests.DTP.class)); static class NameToAnnotationEntry extends AbstractMap.SimpleEntry> { public NameToAnnotationEntry(String key, Class entry) { @@ -531,6 +533,16 @@ R scan(Iterable iter, P p) { int value(); } + @Target({ElementType.TYPE_USE, ElementType.FIELD}) + public @interface DTF { + int value(); + } + + @Target({ElementType.TYPE_USE, ElementType.PARAMETER}) + public @interface DTP { + int value(); + } + // Test cases // TODO: add more cases for arrays @@ -697,6 +709,29 @@ class Inner8<@TA(50) T> { @Test(posn=6, annoType = TB.class, expect = "61") void m60(@TA(60) @TB(61) String t) { } + // Test dual target annotations on uses of type variables + @Test(posn=6, annoType = DTP.class, expect = "61") + void m61(@DTP(61) T t) { } + + @Test(posn=7, annoType = DTP.class, expect = "62") + void m62(@DTP(62) T[] t) { } + + class Inner63 { + @Test(posn=0, annoType = DTF.class, expect = "63") + @DTF(63) T f; + } + // Test dual target annotations on uses of Class types + @Test(posn=6, annoType = DTP.class, expect = "64") + void m64(@DTP(64) String t) { } + + @Test(posn=7, annoType = DTP.class, expect = "65") + void m65(@DTP(65) String[] t) { } + + class Inner66 { + @Test(posn=0, annoType = DTF.class, expect = "66") + @DTF(66) String f; + } + class Inner70 { @Test(posn=0, annoType = TA.class, expect = "70") @Test(posn=0, annoType = TB.class, expect = "71") diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java index 064778b3a2ab..4ee145f923be 100644 --- a/test/lib/RedefineClassHelper.java +++ b/test/lib/RedefineClassHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,13 @@ import java.lang.classfile.ClassElement; import java.lang.classfile.ClassFile; import java.lang.classfile.ClassModel; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeTransform; +import java.lang.classfile.MethodModel; import java.lang.constant.ClassDesc; - import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; +import java.util.function.Predicate; import jdk.test.lib.compiler.InMemoryJavaCompiler; import jdk.test.lib.helpers.ClassFileInstaller; @@ -68,6 +71,10 @@ public static void redefineClass(Class clazz, byte[] bytecode) throws Excepti instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode)); } + private static byte[] getBytecodes(Class clazz) throws Exception { + return getBytecodes(clazz.getClassLoader(), clazz.getName()); + } + private static byte[] getBytecodes(ClassLoader loader, String name) throws Exception { try (InputStream is = loader.getResourceAsStream(name + ".class")) { byte[] buf = is.readAllBytes(); @@ -103,6 +110,33 @@ public static byte[] replaceClassName(ClassLoader loader, String oldClassName, S return replaceClassName(buf, newClassName); } + /* + * For the given clazz, use to replace the code body of the methods that are + * selected by filter. + * + * @param clazz the class to redefine + * @param filter the Predicate to choose the method(s) to redefine + * @param xform used for generating new method bodies + * + * Example: + * + * RedefineClassHelper.redefineMethodBodies(ShouldBeTransformed.class, + * (MethodModel method) -> method.methodName().equalsString("toString"), + * (CodeBuilder builder, CodeElement element) -> { + * builder.ldc("YYYY"); + * builder.areturn(); + * }); + */ + public static void redefineMethodBodies(Class clazz, Predicate filter, CodeTransform xform) throws Exception { + byte[] bytecodes = RedefineClassHelper.getBytecodes(clazz); + ClassFile cf = ClassFile.of(); + ClassModel model = cf.parse(bytecodes); + + ClassTransform transform = + ClassTransform.transformingMethodBodies(filter, xform); + redefineClass(clazz, cf.transformClass(model, transform)); + } + /** * Main method to be invoked before test to create the redefineagent.jar */ diff --git a/test/lib/jdk/test/lib/security/XMLUtils.java b/test/lib/jdk/test/lib/security/XMLUtils.java index e70a30d9b3d2..62090c9c8619 100644 --- a/test/lib/jdk/test/lib/security/XMLUtils.java +++ b/test/lib/jdk/test/lib/security/XMLUtils.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 @@ -187,6 +187,7 @@ public static Signer signer(PrivateKey privateKey) public static class Signer { + private String baseURI = null; final PrivateKey privateKey; // signer key, never null X509Certificate cert; // certificate, optional @@ -253,6 +254,11 @@ public Signer prop(String name, Object o) { return this; } + public Signer baseURI(String base) { + this.baseURI = base; + return this; + } + // Signs different sources // Signs an XML file in detached mode @@ -341,6 +347,9 @@ private DOMSignContext withProps(DOMSignContext ctxt) { for (var e : props.entrySet()) { ctxt.setProperty(e.getKey(), e.getValue()); } + if (baseURI != null) { + ctxt.setBaseURI(baseURI); + } return ctxt; } diff --git a/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java b/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java new file mode 100644 index 000000000000..9cd5b08cdf11 --- /dev/null +++ b/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java @@ -0,0 +1,502 @@ +/* + * 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.valhalla.hash; + +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@Fork(value = 3, jvmArgsAppend = {"--enable-preview"}) +@Warmup(iterations = 3, time = 1) +@Measurement(iterations = 5, time = 1) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@BenchmarkMode(Mode.AverageTime) +@State(Scope.Thread) +public class FastPath { + public static final int SIZE = 100; + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash(Object obj) { + return System.identityHashCode(obj); + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(Empty obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(Byte obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(Short obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(Integer obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(Long obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(MyIntInt obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(MyLongInt obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(MyLongLong obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(WithOop obj) { + return System.identityHashCode(obj); + } + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private static int hash_specialized(MyByteShort obj) { + return System.identityHashCode(obj); + } + + // Homogeneous cases of null, all go to null path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_null() { + int s = 0; + for (int i = 0; i < SIZE; i++) { + s += hash(null); + } + return s; + } + + // Homogeneous cases of empty object, all go to fast path + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + public int no_hoist() { + return hash(new Empty()); + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_empty() { + int s = System.identityHashCode(Empty.class); + for (int i = 0; i < SIZE; i++) { + s += no_hoist(); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_empty_static() { + int s = System.identityHashCode(Empty.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new Empty()); + } + return s; + } + + // Homogeneous cases of bytes, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_byte() { + int s = System.identityHashCode(Byte.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new Byte((byte)i)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_byte_static() { + int s = System.identityHashCode(Byte.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new Byte((byte)i)); + } + return s; + } + + // Homogeneous cases of shorts, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_short() { + int s = System.identityHashCode(Short.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new Short((short)(i + 256))); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_short_static() { + int s = System.identityHashCode(Short.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new Short((short)(i + 256))); + } + return s; + } + + // Homogeneous cases of ints, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_int() { + int s = System.identityHashCode(Integer.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new Integer(i + 256)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_int_static() { + int s = System.identityHashCode(Integer.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new Integer(i + 256)); + } + return s; + } + + // Homogeneous cases of longs, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_long() { + int s = System.identityHashCode(Long.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new Long(i + 256)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_long_static() { + int s = System.identityHashCode(Long.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new Long(i + 256)); + } + return s; + } + + // Homogeneous cases of pairs of ints, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_int_int() { + int s = System.identityHashCode(MyIntInt.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new MyIntInt(i, 2*i)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_int_int_static() { + int s = System.identityHashCode(MyIntInt.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new MyIntInt(i, 2*i)); + } + return s; + } + + // Heterogeneous cases, all go to fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int heterogeneous_small() { + int s = System.identityHashCode(Empty.class); + s += System.identityHashCode(Byte.class); + s += System.identityHashCode(Short.class); + s += System.identityHashCode(Integer.class); + s += System.identityHashCode(Long.class); + s += System.identityHashCode(MyIntInt.class); + for (int i = 0; i < SIZE; i++) { + Object v = switch (i % 7) { + case 0 -> new Empty(); + case 1 -> new Byte((byte) i); + case 2 -> new Short((short) (i + 256)); + case 3 -> new Integer(i + 256); + case 4 -> new Long(i + 256); + case 5 -> new MyIntInt(i, 2 * i); + default -> null; + }; + s += hash(v); + } + return s; + } + + // Homogeneous cases of pairs of long and int, too big for fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_too_big_long_int() { + int s = System.identityHashCode(MyLongInt.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new MyLongInt(i, 2*i)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_too_big_long_int_static() { + int s = System.identityHashCode(MyLongInt.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new MyLongInt(i, 2*i)); + } + return s; + } + + // Homogeneous cases of pairs of long, too big for fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_too_big_long_long() { + int s = System.identityHashCode(MyLongLong.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new MyLongLong(i, 2*i)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_too_big_long_long_static() { + int s = System.identityHashCode(MyLongLong.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new MyLongLong(i, 2*i)); + } + return s; + } + + // Heterogeneous cases, too big for fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int heterogeneous_too_big() { + int s = System.identityHashCode(MyLongInt.class); + s += System.identityHashCode(MyLongLong.class); + for (int i = 0; i < SIZE; i++) { + Object v = switch (i % 2) { + case 0 -> new MyLongInt(i, 2*i); + default -> new MyLongLong(i, 2*i); + }; + s += hash(v); + } + return s; + } + + // Homogeneous cases, with oop, so no fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_with_oop() { + int s = System.identityHashCode(WithOop.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new WithOop(i)); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_with_oop_static() { + int s = System.identityHashCode(WithOop.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new WithOop(i)); + } + return s; + } + + // Homogeneous cases, not a nice size, so no fast path + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_weird_size() { + int s = System.identityHashCode(MyByteShort.class); + for (int i = 0; i < SIZE; i++) { + s += hash(new MyByteShort((byte) i, (short) (2*i))); + } + return s; + } + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_weird_size_static() { + int s = System.identityHashCode(MyByteShort.class); + for (int i = 0; i < SIZE; i++) { + s += hash_specialized(new MyByteShort((byte) i, (short) (2*i))); + } + return s; + } + + // Homogeneous cases of String, so fast path not taken + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_with_obj_string() { + int s = System.identityHashCode(String.class); + s += System.identityHashCode(int[].class); + for (int i = 0; i < SIZE; i++) { + s += hash(String.valueOf(i)); + } + return s; + } + + // Homogeneous cases of arrays, so fast path not taken + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int homogeneous_with_obj_array() { + int s = System.identityHashCode(String.class); + s += System.identityHashCode(int[].class); + for (int i = 0; i < SIZE; i++) { + s += hash(new int[]{i}); + } + return s; + } + + // Heterogeneous array, identity objects, so fast path not taken + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int heterogeneous_with_obj() { + int s = System.identityHashCode(String.class); + s += System.identityHashCode(int[].class); + for (int i = 0; i < SIZE; i++) { + Object v = switch (i % 2) { + case 0 -> String.valueOf(i); + default -> new int[]{i}; + }; + s += hash(v); + } + return s; + } + + // Heterogeneous array, all of the above + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int big_mix() { + int s = System.identityHashCode(Empty.class); + s += System.identityHashCode(Byte.class); + s += System.identityHashCode(Short.class); + s += System.identityHashCode(Integer.class); + s += System.identityHashCode(Long.class); + s += System.identityHashCode(MyIntInt.class); + s += System.identityHashCode(MyLongInt.class); + s += System.identityHashCode(MyLongLong.class); + s += System.identityHashCode(WithOop.class); + s += System.identityHashCode(MyByteShort.class); + s += System.identityHashCode(String.class); + s += System.identityHashCode(int[].class); + for (int i = 0; i < SIZE; i++) { + Object v = switch (i % 13) { + case 0 -> new Empty(); + case 1 -> new Byte((byte)i); + case 2 -> new Short((short)(i + 256)); + case 3 -> new Integer((i + 256)); + case 4 -> new Long((i + 256)); + case 5 -> new MyIntInt(i, 2*i); + case 6 -> new MyLongInt(i, 2*i); + case 7 -> new MyLongLong(i, 2*i); + case 8 -> new WithOop(i); + case 9 -> new MyByteShort((byte) i, (short)(2*i)); + case 10 -> String.valueOf(i); + case 11 -> new int[]{i}; + default -> null; + }; + s += hash(v); + } + return s; + } + + // Array of pre-hashed values, should take the cache path. + @Benchmark + @OperationsPerInvocation(SIZE) + @CompilerControl(CompilerControl.Mode.INLINE) + public int pre_hashed(PreHashedCase st) { + int s = 0; + for (int i = 0; i < SIZE; i++) { + s += hash(st.arr[i]); + } + return s; + } + + static value class Empty {} + static value class MyIntInt { + int fst; + int snd; + public MyIntInt (int fst, int snd) {this.fst = fst; this.snd = snd;} + } + static value class MyLongInt { + long fst; + int snd; + MyLongInt(long fst, int snd) { this.fst = fst; this.snd = snd; } + } + static value class MyLongLong { + long fst; + long snd; + MyLongLong(long fst, long snd) { this.fst = fst; this.snd = snd; } + } + static value class WithOop { + Integer[] s; + WithOop(int i) { + if (i % 4 == 0) { + this.s = null; + } else { + this.s = new Integer[]{i}; + } + } + } + static value class MyByteShort { + byte fst; + short snd; + MyByteShort(byte fst, short snd) { this.fst = fst; this.snd = snd; } + } + + @State(Scope.Thread) + public static class PreHashedCase { + Object[] arr; + + @Setup + public void setup() { + arr = new Object[SIZE]; + + for (int i = 0; i < SIZE; i++) { + arr[i] = new MyLongLong(((long)i) << 32, i); + System.identityHashCode(arr[i]); + } + } + } +}