diff --git a/doc/testing.html b/doc/testing.html index 195153c8612..c8d0b928bb0 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -284,9 +284,10 @@

JTReg

Gtest

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

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

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

diff --git a/doc/testing.md b/doc/testing.md index d0e54aab02b..5f70f2796ad 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -198,8 +198,8 @@ use a fully qualified test descriptor, add `jtreg:`, e.g. **Note:** To be able to run the Gtest suite, you need to configure your build to be able to find a proper version of the gtest source. For details, see the -section ["Running Tests" in the build -documentation](building.html#running-tests). +section **"Running Tests" in the build +documentation** ([html](building.html#running-tests), [markdown](building.md#running-tests)). Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just `gtest`, or as a fully qualified test descriptor diff --git a/make/autoconf/toolchain_microsoft.m4 b/make/autoconf/toolchain_microsoft.m4 index f577cf1a2a1..afe04acf029 100644 --- a/make/autoconf/toolchain_microsoft.m4 +++ b/make/autoconf/toolchain_microsoft.m4 @@ -217,10 +217,12 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], [$PROGRAMFILES_X86/$VS_INSTALL_DIR], [well-known name]) fi + # Derive system drive root from CMD (which is at /windows/system32/cmd.exe) + WINSYSDRIVE_ROOT="$(dirname "$(dirname "$(dirname "$CMD")")")" TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files/$VS_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files/$VS_INSTALL_DIR], [well-known name]) TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files (x86)/$VS_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files (x86)/$VS_INSTALL_DIR], [well-known name]) if test "x$SDK_INSTALL_DIR" != x; then if test "x$ProgramW6432" != x; then TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], @@ -235,9 +237,9 @@ AC_DEFUN([TOOLCHAIN_FIND_VISUAL_STUDIO_BAT_FILE], [$PROGRAMFILES/$SDK_INSTALL_DIR], [well-known name]) fi TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files/$SDK_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files/$SDK_INSTALL_DIR], [well-known name]) TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT([$TARGET_CPU], [$VS_VERSION], - [c:/program files (x86)/$SDK_INSTALL_DIR], [well-known name]) + [$WINSYSDRIVE_ROOT/program files (x86)/$SDK_INSTALL_DIR], [well-known name]) fi VCVARS_VER=auto @@ -338,7 +340,7 @@ AC_DEFUN([TOOLCHAIN_EXTRACT_VISUAL_STUDIO_ENV], OLDPATH="$PATH" # Make sure we only capture additions to PATH needed by VS. # Clear out path, but need system dir present for vsvars cmd file to be able to run - export PATH=$WINENV_PREFIX/c/windows/system32 + export PATH="$(dirname "$CMD")" # The "| cat" is to stop SetEnv.Cmd to mess with system colors on some systems # We can't pass -vcvars_ver=$VCVARS_VER here because cmd.exe eats all '=' # in bat file arguments. :-( diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index ab878a4d2a5..de496b3f606 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -87,6 +87,7 @@ public class CLDRConverter { static final String EXEMPLAR_CITY_PREFIX = "timezone.excity."; static final String ZONE_NAME_PREFIX = "timezone.displayname."; static final String METAZONE_ID_PREFIX = "metazone.id."; + static final String METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset."; static final String PARENT_LOCALE_PREFIX = "parentLocale."; static final String LIKELY_SCRIPT_PREFIX = "likelyScript."; static final String META_EMPTY_ZONE_NAME = "EMPTY_ZONE"; @@ -139,6 +140,11 @@ public class CLDRConverter { private static final Map tzdbSubstLetters = HashMap.newHashMap(512); private static final Map tzdbLinks = HashMap.newHashMap(512); + // Map of explicit dst offsets for metazones + // key: time zone ID + // value: explicit dstOffset for the corresponding metazone name + static final Map explicitDstOffsets = HashMap.newHashMap(32); + static enum DraftType { UNCONFIRMED, PROVISIONAL, @@ -867,6 +873,12 @@ private static Map extractZoneNames(Map map, Str .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); names.putAll(exCities); + // Explicit metazone offsets + if (id.equals("root")) { + explicitDstOffsets.forEach((k, v) -> + names.put(METAZONE_DSTOFFSET_PREFIX + k, v)); + } + // If there's no UTC entry at this point, add an empty one if (!names.isEmpty() && !names.containsKey("UTC")) { names.putIfAbsent(METAZONE_ID_PREFIX + META_EMPTY_ZONE_NAME, EMPTY_ZONE); diff --git a/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java index 2c3757b7a47..45de46d2476 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/MetaZonesParseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,7 +84,15 @@ public void startElement(String uri, String localName, String qName, Attributes if (fromLDT.isBefore(now) && toLDT.isAfter(now)) { metazone = attributes.getValue("mzone"); + + // Explicit metazone DST offsets. Only the "dst" offset is needed, + // as "std" is used by default when it doesn't match. + String dstOffset = attributes.getValue("dstOffset"); + if (dstOffset != null) { + CLDRConverter.explicitDstOffsets.put(tzid, dstOffset); + } } + pushIgnoredContainer(qName); break; diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 3953f38f653..8278bf6bcfa 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -198,7 +198,8 @@ public void generateBundle(String packageName, String baseName, String localeID, } else if (value instanceof String) { String valStr = (String)value; if (type == BundleType.TIMEZONE && - !key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || + !(key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || + key.startsWith(CLDRConverter.METAZONE_DSTOFFSET_PREFIX)) || valStr.startsWith(META_VALUE_PREFIX)) { out.printf(" { \"%s\", %s },\n", key, CLDRConverter.saveConvert(valStr, useJava)); } else { diff --git a/make/langtools/tools/previewfeature/SetupPreviewFeature.java b/make/langtools/tools/previewfeature/SetupPreviewFeature.java index 2d5207f0e17..5f9b00edc6d 100644 --- a/make/langtools/tools/previewfeature/SetupPreviewFeature.java +++ b/make/langtools/tools/previewfeature/SetupPreviewFeature.java @@ -30,6 +30,7 @@ import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @@ -76,7 +77,7 @@ public static void main(String... args) throws Exception { var target = Path.of(args[1]); Files.createDirectories(target.getParent()); if (constantsToAdd.isEmpty()) { - Files.copy(source, target); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); } else { String sourceCode = Files.readString(source); try (var out = Files.newBufferedWriter(target)) { diff --git a/make/scripts/fixpath.sh b/make/scripts/fixpath.sh index 6a524df4c68..78690f1f2cc 100644 --- a/make/scripts/fixpath.sh +++ b/make/scripts/fixpath.sh @@ -88,7 +88,10 @@ function setup() { fi if [[ -z ${CMD+x} ]]; then - CMD="$DRIVEPREFIX/c/windows/system32/cmd.exe" + CMD="$(type -p cmd.exe 2>/dev/null)" + if [[ -z "$CMD" ]]; then + CMD="$DRIVEPREFIX/c/windows/system32/cmd.exe" + fi fi if [[ -z ${WINTEMP+x} ]]; then diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 05b2514a456..53fa4e3066c 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1182,12 +1182,12 @@ class CallStubImpl { public: // Size of call trampoline stub. static uint size_call_trampoline() { - return 0; // no call trampolines on this platform + return MacroAssembler::max_trampoline_stub_size(); } // number of relocations needed by a call trampoline stub static uint reloc_call_trampoline() { - return 0; // no call trampolines on this platform + return 5; // metadata; call dest; trampoline address; trampoline destination; trampoline_owner_metadata } }; diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index e7d8c2d3648..4de6237304d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -42,6 +42,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/powerOfTwo.hpp" #include "vmreg_aarch64.inline.hpp" @@ -520,6 +521,10 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod #if INCLUDE_CDS if (AOTCodeCache::is_on_for_dump()) { address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } if (AOTRuntimeConstants::contains(b)) { __ load_aotrc_address(dest->as_register_lo(), b); break; diff --git a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp index 6fe3315014b..640cd495383 100644 --- a/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compiledIC_aarch64.cpp @@ -89,16 +89,21 @@ void CompiledDirectCall::set_to_interpreted(const methodHandle& callee, address NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + NativeInstruction::instruction_size); + // In AOT "production" run we have mixture of AOTed and normal JITed code. + // Static call stub in AOTed nmethod always has far jump. + // Normal JITed nmethod may have short or far jump depending on distance. + // Determine actual jump instruction we have in code. + address next_instr = method_holder->next_instruction_address(); + bool is_general_jump = nativeInstruction_at(next_instr)->is_general_jump(); + #ifdef ASSERT - NativeJump* jump = MacroAssembler::codestub_branch_needs_far_jump() - ? nativeGeneralJump_at(method_holder->next_instruction_address()) - : nativeJump_at(method_holder->next_instruction_address()); + NativeJump* jump = is_general_jump ? nativeGeneralJump_at(next_instr) : nativeJump_at(next_instr); verify_mt_safe(callee, entry, method_holder, jump); #endif // Update stub. method_holder->set_data((intptr_t)callee()); - MacroAssembler::pd_patch_instruction(method_holder->next_instruction_address(), entry); + MacroAssembler::pd_patch_instruction(next_instr, entry); ICache::invalidate_range(stub, to_interp_stub_size()); // Update jump to call. set_destination_mt_safe(stub); diff --git a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp index 0bfc320179d..a9c320912cb 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.cpp @@ -56,8 +56,10 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d } } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { - +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst, Register tmp1, Register tmp2) { + precond(tmp1 != noreg); + precond(tmp2 != noreg); + assert_different_registers(obj, tmp1, tmp2); BarrierSet* bs = BarrierSet::barrier_set(); assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); @@ -65,16 +67,16 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob assert(CardTable::dirty_card_val() == 0, "must be"); - __ load_byte_map_base(rscratch1); + __ load_byte_map_base(tmp1); if (UseCondCardMark) { Label L_already_dirty; - __ ldrb(rscratch2, Address(obj, rscratch1)); - __ cbz(rscratch2, L_already_dirty); - __ strb(zr, Address(obj, rscratch1)); + __ ldrb(tmp2, Address(obj, tmp1)); + __ cbz(tmp2, L_already_dirty); + __ strb(zr, Address(obj, tmp1)); __ bind(L_already_dirty); } else { - __ strb(zr, Address(obj, rscratch1)); + __ strb(zr, Address(obj, tmp1)); } } @@ -112,10 +114,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || (dst.index() == noreg && dst.offset() == 0)) { - store_check(masm, dst.base(), dst); + store_check(masm, dst.base(), dst, tmp1, tmp2); } else { __ lea(tmp3, dst); - store_check(masm, tmp3, dst); + store_check(masm, tmp3, dst, tmp1, tmp2); } } } diff --git a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp index 07dd8eb5565..e05e9e90e5d 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shared/cardTableBarrierSetAssembler_aarch64.hpp @@ -46,7 +46,7 @@ class CardTableBarrierSetAssembler: public BarrierSetAssembler { virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); - void store_check(MacroAssembler* masm, Register obj, Address dst); + void store_check(MacroAssembler* masm, Register obj, Address dst, Register tmp1, Register tmp2); }; #endif // CPU_AARCH64_GC_SHARED_CARDTABLEBARRIERSETASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 4f0977a414f..f0885fee93d 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -879,7 +879,9 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - ICache::invalidate_word((address)patch_addr); + if (!UseSingleICacheInvalidation) { + ICache::invalidate_word((address)patch_addr); + } } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 0ca5cb25e0c..dfeba73bede 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -131,6 +131,10 @@ define_pd_global(intx, InlineSmallCode, 1000); "Branch Protection to use: none, standard, pac-ret") \ product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \ "Always merge DMB instructions in code emission") \ + product(bool, NeoverseN1ICacheErratumMitigation, false, DIAGNOSTIC, \ + "Enable workaround for Neoverse N1 erratum 1542419") \ + product(bool, UseSingleICacheInvalidation, false, DIAGNOSTIC, \ + "Defer multiple ICache invalidation to single invalidation") \ // end of ARCH_FLAGS diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index ebbc35ce20a..7fa2e8086ad 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -952,7 +952,10 @@ void MacroAssembler::emit_static_call_stub() { } int MacroAssembler::static_call_stub_size() { - if (!codestub_branch_needs_far_jump()) { + // During AOT production run AOT and JIT compiled code + // are used at the same time. We need this size + // to be the same for both types of code. + if (!codestub_branch_needs_far_jump() && !AOTCodeCache::is_on_for_use()) { // isb; movk; movz; movz; b return 5 * NativeInstruction::instruction_size; } @@ -3451,7 +3454,7 @@ void MacroAssembler::subw(Register Rd, Register Rn, RegisterOrConstant decrement void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { - if (Universe::is_fully_initialized()) { + if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) { mov(rheapbase, CompressedOops::base()); } else { lea(rheapbase, ExternalAddress(CompressedOops::base_addr())); @@ -5125,7 +5128,8 @@ void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { if (CompressedKlassPointers::base() == nullptr) { cmp(klass, tmp, LSL, CompressedKlassPointers::shift()); return; - } else if (((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 + } else if (!AOTCodeCache::is_on_for_dump() && + ((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 && CompressedKlassPointers::shift() == 0) { // Only the bottom 32 bits matter cmpw(klass, tmp); @@ -5368,7 +5372,7 @@ void MacroAssembler::encode_klass_not_null_for_aot(Register dst, Register src) { } void MacroAssembler::encode_klass_not_null(Register dst, Register src) { - if (AOTCodeCache::is_on_for_dump()) { + if (CompressedKlassPointers::base() != nullptr && AOTCodeCache::is_on_for_dump()) { encode_klass_not_null_for_aot(dst, src); return; } diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index dbec2d76d4f..f1b9fb213a2 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -54,7 +54,12 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { bytes = MacroAssembler::pd_patch_instruction_size(addr(), x); break; } - ICache::invalidate_range(addr(), bytes); + + if (UseSingleICacheInvalidation) { + assert(_binding != nullptr, "expect to be called with RelocIterator in use"); + } else { + ICache::invalidate_range(addr(), bytes); + } } address Relocation::pd_call_destination(address orig_addr) { diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index e36aa21b567..638e57b03fe 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -290,7 +290,7 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); const char* name = OptoRuntime::stub_name(StubId::c2_exception_id); - CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, (uint)BlobId::c2_exception_id, name); + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_exception_id); if (blob != nullptr) { return blob->as_exception_blob(); } diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index 695534604b8..9dac6a39b82 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -84,8 +84,7 @@ do_stub(compiler, count_positives) \ do_arch_entry(aarch64, compiler, count_positives, count_positives, \ count_positives) \ - do_stub(compiler, count_positives_long) \ - do_arch_entry(aarch64, compiler, count_positives_long, \ + do_arch_entry(aarch64, compiler, count_positives, \ count_positives_long, count_positives_long) \ do_stub(compiler, compare_long_string_LL) \ do_arch_entry(aarch64, compiler, compare_long_string_LL, \ @@ -108,8 +107,9 @@ do_stub(compiler, string_indexof_linear_ul) \ do_arch_entry(aarch64, compiler, string_indexof_linear_ul, \ string_indexof_linear_ul, string_indexof_linear_ul) \ - /* this uses the entry for ghash_processBlocks */ \ - do_stub(compiler, ghash_processBlocks_wide) \ + do_stub(compiler, ghash_processBlocks_small) \ + do_arch_entry(aarch64, compiler, ghash_processBlocks_small, \ + ghash_processBlocks_small, ghash_processBlocks_small) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ @@ -139,9 +139,49 @@ do_stub(final, spin_wait) \ do_arch_entry_init(aarch64, final, spin_wait, spin_wait, \ spin_wait, empty_spin_wait) \ - /* stub only -- entries are not stored in StubRoutines::aarch64 */ \ /* n.b. these are not the same as the generic atomic stubs */ \ do_stub(final, atomic_entry_points) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_4_impl, atomic_fetch_add_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_8_impl, atomic_fetch_add_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_4_relaxed_impl, \ + atomic_fetch_add_4_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_fetch_add_8_relaxed_impl, \ + atomic_fetch_add_8_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_xchg_4_impl, atomic_xchg_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_xchg_8_impl, atomic_xchg_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_1_impl, atomic_cmpxchg_1_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_impl, atomic_cmpxchg_4_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_impl, atomic_cmpxchg_8_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_1_relaxed_impl, \ + atomic_cmpxchg_1_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_relaxed_impl, \ + atomic_cmpxchg_4_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_relaxed_impl, \ + atomic_cmpxchg_8_relaxed_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_release_impl, \ + atomic_cmpxchg_4_release_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_release_impl, \ + atomic_cmpxchg_8_release_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_4_seq_cst_impl, \ + atomic_cmpxchg_4_seq_cst_impl) \ + do_arch_entry(aarch64, final, atomic_entry_points, \ + atomic_cmpxchg_8_seq_cst_impl, \ + atomic_cmpxchg_8_seq_cst_impl) \ #endif // CPU_AARCH64_STUBDECLARATIONS_HPP diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 21a1124a8ec..32fd8afb268 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -79,6 +79,166 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") +// Constant data definitions + +static const uint32_t _sha256_round_consts[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + +static const uint64_t _sha512_round_consts[80] = { + 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, + 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, + 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, + 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, + 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, + 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, + 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, + 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, + 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, + 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, + 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, + 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, + 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, + 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, + 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, + 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, + 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, + 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, + 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, + 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, + 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, + 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, + 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, + 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, + 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, + 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, + 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L +}; + +static const uint64_t _sha3_round_consts[24] = { + 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, + 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, + 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, + 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, + 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L +}; + +static const uint64_t _double_keccak_round_consts[24] = { + 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, + 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, + 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, + 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, + 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L +}; + +static const char _encodeBlock_toBase64[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +static const char _encodeBlock_toBase64URL[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' +}; + +// Non-SIMD lookup tables are mostly dumped from fromBase64 array used in java.util.Base64, +// except the trailing character '=' is also treated illegal value in this intrinsic. That +// is java.util.Base64.fromBase64['='] = -2, while fromBase(URL)64ForNoSIMD['='] = 255 here. +static const uint8_t _decodeBlock_fromBase64ForNoSIMD[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +static const uint8_t _decodeBlock_fromBase64URLForNoSIMD[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, +}; + +// A legal value of base64 code is in range [0, 127]. We need two lookups +// with tbl/tbx and combine them to get the decode data. The 1st table vector +// lookup use tbl, out of range indices are set to 0 in destination. The 2nd +// table vector lookup use tbx, out of range indices are unchanged in +// destination. Input [64..126] is mapped to index [65, 127] in second lookup. +// The value of index 64 is set to 0, so that we know that we already get the +// decoded data with the 1st lookup. +static const uint8_t _decodeBlock_fromBase64ForSIMD[128] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, + 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, + 255u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, + 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, +}; + +static const uint8_t _decodeBlock_fromBase64URLForSIMD[128] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, + 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, + 63u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, + 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, +}; + + // Stub Code definitions class StubGenerator: public StubCodeGenerator { @@ -203,8 +363,17 @@ class StubGenerator: public StubCodeGenerator { "adjust this code"); StubId stub_id = StubId::stubgen_call_stub_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, "expected 1 extra entry"); + return_address = entries.at(0); + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Address sp_after_call (rfp, sp_after_call_off * wordSize); @@ -323,6 +492,7 @@ class StubGenerator: public StubCodeGenerator { // save current address for use by exception handling code return_address = __ pc(); + entries.append(return_address); // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) @@ -406,6 +576,9 @@ class StubGenerator: public StubCodeGenerator { __ strd(j_farg0, Address(j_rarg2, 0)); __ br(Assembler::AL, exit); + // record the stub entry and end plus the auxiliary entry + store_archive_data(stub_id, start, __ pc(), &entries); + return start; } @@ -423,8 +596,14 @@ class StubGenerator: public StubCodeGenerator { address generate_catch_exception() { StubId stub_id = StubId::stubgen_catch_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_call_stub(): const Address sp_after_call(rfp, sp_after_call_off * wordSize); @@ -450,7 +629,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(r0); __ str(r0, Address(rthread, Thread::pending_exception_offset())); - __ mov(rscratch1, (address)__FILE__); + // special case -- add file name string to AOT address table + address file = (address)AOTCodeCache::add_C_string(__FILE__); + __ lea(rscratch1, ExternalAddress(file)); __ str(rscratch1, Address(rthread, Thread::exception_file_offset())); __ movw(rscratch1, (int)__LINE__); __ strw(rscratch1, Address(rthread, Thread::exception_line_offset())); @@ -458,7 +639,10 @@ class StubGenerator: public StubCodeGenerator { // complete return to VM assert(StubRoutines::_call_stub_return_address != nullptr, "_call_stub_return_address must have been generated before"); - __ b(StubRoutines::_call_stub_return_address); + __ b(RuntimeAddress(StubRoutines::_call_stub_return_address)); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); return start; } @@ -479,8 +663,14 @@ class StubGenerator: public StubCodeGenerator { address generate_forward_exception() { StubId stub_id = StubId::stubgen_forward_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Upon entry, LR points to the return address returning into // Java (interpreted or compiled) code; i.e., the return address @@ -551,6 +741,9 @@ class StubGenerator: public StubCodeGenerator { __ verify_oop(r0); __ br(r19); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -569,8 +762,14 @@ class StubGenerator: public StubCodeGenerator { // [tos + 5]: saved rscratch1 address generate_verify_oop() { StubId stub_id = StubId::stubgen_verify_oop_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label exit, error; @@ -613,14 +812,23 @@ class StubGenerator: public StubCodeGenerator { __ blr(rscratch1); __ hlt(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Generate indices for iota vector. address generate_iota_indices(StubId stub_id) { + 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(); // B __ emit_data64(0x0706050403020100, relocInfo::none); __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); @@ -639,6 +847,10 @@ class StubGenerator: public StubCodeGenerator { // D - FP __ emit_data64(0x0000000000000000, relocInfo::none); // 0.0d __ emit_data64(0x3FF0000000000000, relocInfo::none); // 1.0d + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -656,15 +868,21 @@ class StubGenerator: public StubCodeGenerator { // r11 < MacroAssembler::zero_words_block_size. address generate_zero_blocks() { + StubId stub_id = StubId::stubgen_zero_blocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); Label done; Label base_aligned; Register base = r10, cnt = r11; - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_zero_blocks_id; - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (UseBlockZeroing) { int zva_length = VM_Version::zva_length(); @@ -707,6 +925,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -803,6 +1024,12 @@ class StubGenerator: public StubCodeGenerator { // s and d are adjusted to point to the remaining words to copy // address generate_copy_longs(StubId stub_id, DecoratorSet decorators, Register s, Register d, Register count) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } BasicType type; copy_direction direction; @@ -854,7 +1081,7 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label unaligned_copy_long; if (AvoidUnalignedAccesses) { @@ -1154,6 +1381,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1445,19 +1675,25 @@ class StubGenerator: public StubCodeGenerator { } if (direction == copy_forwards) { if (type != T_OBJECT) { - __ bl(StubRoutines::aarch64::copy_byte_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_byte_f())); + __ blr(rscratch1); } else if ((decorators & IS_DEST_UNINITIALIZED) != 0) { - __ bl(StubRoutines::aarch64::copy_oop_uninit_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_uninit_f())); + __ blr(rscratch1); } else { - __ bl(StubRoutines::aarch64::copy_oop_f()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_f())); + __ blr(rscratch1); } } else { if (type != T_OBJECT) { - __ bl(StubRoutines::aarch64::copy_byte_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_byte_b())); + __ blr(rscratch1); } else if ((decorators & IS_DEST_UNINITIALIZED) != 0) { - __ bl(StubRoutines::aarch64::copy_oop_uninit_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_uninit_b())); + __ blr(rscratch1); } else { - __ bl(StubRoutines::aarch64::copy_oop_b()); + __ lea(rscratch1, RuntimeAddress(StubRoutines::aarch64::copy_oop_b())); + __ blr(rscratch1); } } @@ -1508,8 +1744,8 @@ class StubGenerator: public StubCodeGenerator { // stub_id - is used to name the stub and identify all details of // how to perform the copy. // - // entry - is assigned to the stub's post push entry point unless - // it is null + // nopush_entry - is assigned to the stub's post push entry point + // unless it is null // // Inputs: // c_rarg0 - source array address @@ -1525,8 +1761,6 @@ class StubGenerator: public StubCodeGenerator { // copy method // address generate_disjoint_copy(StubId stub_id, address *nopush_entry) { - Register s = c_rarg0, d = c_rarg1, count = c_rarg2; - RegSet saved_reg = RegSet::of(s, d, count); int size; bool aligned; bool is_oop; @@ -1607,17 +1841,45 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); break; } + // all stubs provide a 2nd entry which omits the frame push for + // use when bailing out from a conjoint copy. However we may also + // need some extra addressses for memory access protection. + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + assert(nopush_entry != nullptr, "all disjoint copy stubs export a nopush entry"); + + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
entries; + GrowableArray
extras; + GrowableArray
*extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, &entries, extras_ptr); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + *nopush_entry = entries.at(0); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + + Register s = c_rarg0, d = c_rarg1, count = c_rarg2; + RegSet saved_reg = RegSet::of(s, d, count); __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); - if (nopush_entry != nullptr) { - *nopush_entry = __ pc(); - // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) - BLOCK_COMMENT("Entry:"); - } + *nopush_entry = __ pc(); + entries.append(*nopush_entry); + + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + BLOCK_COMMENT("Post-Push Entry:"); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; if (dest_uninitialized) { @@ -1636,8 +1898,7 @@ class StubGenerator: public StubCodeGenerator { } { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, size); } @@ -1652,6 +1913,20 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mov(r0, zr); // return 0 __ ret(lr); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count + , "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, &entries, extras_ptr); + return start; } @@ -1663,8 +1938,8 @@ class StubGenerator: public StubCodeGenerator { // corresponding disjoint copy routine which can be // jumped to if the ranges do not actually overlap // - // entry - is assigned to the stub's post push entry point unless - // it is null + // nopush_entry - is assigned to the stub's post push entry point + // unless it is null // // // Inputs: @@ -1681,8 +1956,6 @@ class StubGenerator: public StubCodeGenerator { // used by some other conjoint copy method // address generate_conjoint_copy(StubId stub_id, address nooverlap_target, address *nopush_entry) { - Register s = c_rarg0, d = c_rarg1, count = c_rarg2; - RegSet saved_regs = RegSet::of(s, d, count); int size; bool aligned; bool is_oop; @@ -1762,15 +2035,47 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } + // only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + + // We need to protect memory accesses in certain cases + bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size); + int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT); + GrowableArray
entries; + GrowableArray
extras; + GrowableArray
*entries_ptr = (nopush_entry != nullptr ? &entries : nullptr); + GrowableArray
*extras_ptr = (extra_count > 0 ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entries count %d", entries.length()); + assert(extras.length() == extra_count, + "unexpected extra count %d", extras.length()); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + if (add_extras) { + // register one handler at offset 0 + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } + Register s = c_rarg0, d = c_rarg1, count = c_rarg2; + RegSet saved_regs = RegSet::of(s, d, count); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) - BLOCK_COMMENT("Entry:"); + BLOCK_COMMENT("Post-Push Entry:"); } // use fwd copy when (d-s) above_equal (count*size) @@ -1798,8 +2103,7 @@ class StubGenerator: public StubCodeGenerator { } { // UnsafeMemoryAccess page error: continue after unsafe access - bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size); - UnsafeMemoryAccessMark umam(this, add_entry, true); + UnsafeMemoryAccessMark umam(this, add_extras, true); copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, -size); } if (is_oop) { @@ -1811,6 +2115,23 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ mov(r0, zr); // return 0 __ ret(lr); + + assert(entries.length() == expected_entry_count - 1, + "unexpected entries count %d", entries.length()); + + address end = __ pc(); + + if (add_extras) { + // retrieve the registered handler addresses + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + } + + // record the stub entry and end plus any no_push entry and/or + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -1864,6 +2185,27 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); } + // The normal stub provides a 2nd entry which omits the frame push + // for use when bailing out from a disjoint copy. + // Only some conjoint stubs generate a 2nd entry + int entry_count = StubInfo::entry_count(stub_id); + int expected_entry_count = (nopush_entry == nullptr ? 1 : 2); + GrowableArray
entries; + GrowableArray
*entries_ptr = (expected_entry_count == 1 ? nullptr : &entries); + assert(entry_count == expected_entry_count, + "expected entry count %d does not match declared entry count %d for stub %s", + expected_entry_count, entry_count, StubInfo::name(stub_id)); + address start = load_archive_data(stub_id, entries_ptr); + if (start != nullptr) { + assert(entries.length() + 1 == expected_entry_count, + "expected entry count %d does not match return entry count %d for stub %s", + expected_entry_count, entries.length() + 1, StubInfo::name(stub_id)); + if (nopush_entry != nullptr) { + *nopush_entry = entries.at(0); + } + return start; + } + Label L_load_element, L_store_element, L_do_card_marks, L_done, L_done_pop; // Input registers (after setup_arg_regs) @@ -1896,7 +2238,7 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -1913,6 +2255,7 @@ class StubGenerator: public StubCodeGenerator { // Caller of this entry point must set up the argument registers. if (nopush_entry != nullptr) { *nopush_entry = __ pc(); + entries.append(*nopush_entry); BLOCK_COMMENT("Entry:"); } @@ -2010,6 +2353,8 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end plus any no_push entry + store_archive_data(stub_id, start, __ pc() , entries_ptr); return start; } @@ -2072,13 +2417,18 @@ class StubGenerator: public StubCodeGenerator { address int_copy_entry, address long_copy_entry) { StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_long_aligned, L_int_aligned, L_short_aligned; Register s = c_rarg0, d = c_rarg1, count = c_rarg2; __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame // bump this on entry, not on exit: @@ -2104,6 +2454,9 @@ class StubGenerator: public StubCodeGenerator { __ lsr(count, count, LogBytesPerLong); // size => long_count __ b(RuntimeAddress(long_copy_entry)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2125,7 +2478,12 @@ class StubGenerator: public StubCodeGenerator { address int_copy_entry, address oop_copy_entry, address long_copy_entry, address checkcast_copy_entry) { StubId stub_id = StubId::stubgen_generic_arraycopy_id; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_failed, L_objArray; Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs; @@ -2144,7 +2502,7 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2383,6 +2741,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2427,10 +2788,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); @@ -2563,15 +2929,32 @@ class StubGenerator: public StubCodeGenerator { __ bind(L_exit2); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_unsafecopy_common_error_exit() { - address start_pc = __ pc(); + StubId stub_id = StubId::stubgen_unsafecopy_common_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); __ leave(); __ mov(r0, 0); __ ret(lr); - return start_pc; + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; } // @@ -2589,13 +2972,28 @@ class StubGenerator: public StubCodeGenerator { // c_rarg2 - byte value // address generate_unsafe_setmemory() { + StubId stub_id = StubId::stubgen_unsafe_setmemory_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + // we expect one set of extra unsafememory access handler entries + GrowableArray
extras; + int extra_count = 1 * UnsafeMemoryAccess::COLUMN_COUNT; + address start = load_archive_data(stub_id, nullptr, &extras); + if (start != nullptr) { + assert(extras.length() == extra_count, + "unexpected extra entry count %d", extras.length()); + register_unsafe_access_handlers(extras, 0, 1); + return start; + } + __ align(CodeEntryAlignment); - StubCodeMark mark(this, StubId::stubgen_unsafe_setmemory_id); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); Register dest = c_rarg0, count = c_rarg1, value = c_rarg2; Label tail; + { UnsafeMemoryAccessMark umam(this, true, false); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2679,6 +3077,17 @@ class StubGenerator: public StubCodeGenerator { __ bind(finished); __ leave(); __ ret(lr); + // have to exit the block and destroy the UnsafeMemoryAccessMark + // in order to retrieve the handler end address + } + + // install saved handler addresses in extras + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == extra_count, + "incorrect handlers count %d", extras.length()); + // record the stub entry and end plus the extras + store_archive_data(stub_id, start, end, nullptr, &extras); return start; } @@ -2686,33 +3095,45 @@ class StubGenerator: public StubCodeGenerator { address generate_data_cache_writeback() { const Register line = c_rarg0; // address of line to write back - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); __ cache_wb(Address(line, 0)); __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_data_cache_writeback_sync() { + StubId stub_id = StubId::stubgen_data_cache_writeback_sync_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } const Register is_pre = c_rarg0; // pre or post sync - __ align(CodeEntryAlignment); - - StubId stub_id = StubId::stubgen_data_cache_writeback_sync_id; StubCodeMark mark(this, stub_id); // pre wbsync is a no-op // post wbsync translates to an sfence Label skip; - address start = __ pc(); + start = __ pc(); __ enter(); __ cbnz(is_pre, skip); __ cache_wbsync(false); @@ -2720,6 +3141,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2882,8 +3306,15 @@ class StubGenerator: public StubCodeGenerator { // c_rarg2 - sessionKe (key) in little endian int array // address generate_aescrypt_encryptBlock() { - __ align(CodeEntryAlignment); + assert(UseAES, "need AES cryptographic extension support"); StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); const Register from = c_rarg0; // source array address @@ -2891,7 +3322,7 @@ class StubGenerator: public StubCodeGenerator { const Register key = c_rarg2; // key array address const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2904,6 +3335,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2916,8 +3350,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_aescrypt_decryptBlock() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; @@ -2926,7 +3366,7 @@ class StubGenerator: public StubCodeGenerator { const Register key = c_rarg2; // key array address const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame __ ldrw(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); @@ -2938,6 +3378,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2955,8 +3398,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_loadkeys_44, L_loadkeys_52, L_aes_loop, L_rounds_44, L_rounds_52; @@ -2969,7 +3418,7 @@ class StubGenerator: public StubCodeGenerator { const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); @@ -3043,6 +3492,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3060,8 +3512,14 @@ class StubGenerator: public StubCodeGenerator { // address generate_cipherBlockChaining_decryptAESCrypt() { assert(UseAES, "need AES cryptographic extension support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_loadkeys_44, L_loadkeys_52, L_aes_loop, L_rounds_44, L_rounds_52; @@ -3074,7 +3532,7 @@ class StubGenerator: public StubCodeGenerator { const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) const Register keylen = rscratch1; - address start = __ pc(); + start = __ pc(); __ enter(); @@ -3152,6 +3610,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3188,6 +3649,13 @@ class StubGenerator: public StubCodeGenerator { // r0 - input length // address generate_counterMode_AESCrypt() { + StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } const Register in = c_rarg0; const Register out = c_rarg1; const Register key = c_rarg2; @@ -3248,9 +3716,8 @@ class StubGenerator: public StubCodeGenerator { // Wide bulk encryption of whole blocks. __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; StubCodeMark mark(this, stub_id); - const address start = __ pc(); + start = __ pc(); __ enter(); Label DONE, CTR_large_block, large_block_return; @@ -3435,6 +3902,9 @@ class StubGenerator: public StubCodeGenerator { __ strw(used, Address(used_ptr)); __ b(large_block_return); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3451,11 +3921,16 @@ class StubGenerator: public StubCodeGenerator { // return - number of processed bytes address generate_galoisCounterMode_AESCrypt() { Label ghash_polynomial; // local data generated after code - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register in = c_rarg0; @@ -3567,6 +4042,9 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3685,10 +4163,16 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3815,6 +4299,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3838,11 +4325,16 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3919,6 +4411,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int32(0x8f1bbcdc); __ emit_int32(0xca62c1d6); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3943,30 +4438,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint32_t round_consts[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -3987,7 +4467,7 @@ class StubGenerator: public StubCodeGenerator { // t1 == v7 // load 16 keys to v16..v31 - __ lea(rscratch1, ExternalAddress((address)round_consts)); + __ lea(rscratch1, ExternalAddress((address)_sha256_round_consts)); __ ld1(v16, v17, v18, v19, __ T4S, __ post(rscratch1, 64)); __ ld1(v20, v21, v22, v23, __ T4S, __ post(rscratch1, 64)); __ ld1(v24, v25, v26, v27, __ T4S, __ post(rscratch1, 64)); @@ -4048,6 +4528,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4099,41 +4582,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[80] = { - 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, - 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, - 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, - 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, - 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, - 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, - 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, - 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, - 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, - 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, - 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, - 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, - 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, - 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, - 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, - 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, - 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, - 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, - 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, - 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, - 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, - 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, - 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, - 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, - 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, - 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, - 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -4151,7 +4608,7 @@ class StubGenerator: public StubCodeGenerator { __ ld1(v8, v9, v10, v11, __ T2D, state); // load first 4 round constants - __ lea(rscratch1, ExternalAddress((address)round_consts)); + __ lea(rscratch1, ExternalAddress((address)_sha512_round_consts)); __ ld1(v20, v21, v22, v23, __ T2D, __ post(rscratch1, 64)); __ BIND(sha512_loop); @@ -4236,6 +4693,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4349,22 +4809,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); - StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -4396,7 +4849,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(rscratch2, 24); // load round_constants base - __ lea(rscratch1, ExternalAddress((address) round_consts)); + __ lea(rscratch1, ExternalAddress((address) _sha3_round_consts)); // load input __ ld1(v25, v26, v27, v28, __ T8B, __ post(buf, 32)); @@ -4488,6 +4941,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4495,22 +4951,18 @@ class StubGenerator: public StubCodeGenerator { // c_rarg0 - long[] state0 // c_rarg1 - long[] state1 address generate_double_keccak() { - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + StubId stub_id = StubId::stubgen_double_keccak_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } // Implements the double_keccak() method of the // sun.secyrity.provider.SHA3Parallel class __ align(CodeEntryAlignment); - StubCodeMark mark(this, "StubRoutines", "double_keccak"); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); __ enter(); Register state0 = c_rarg0; @@ -4546,7 +4998,7 @@ class StubGenerator: public StubCodeGenerator { __ movw(rscratch2, 24); // load round_constants base - __ lea(rscratch1, ExternalAddress((address) round_consts)); + __ lea(rscratch1, ExternalAddress((address) _double_keccak_round_consts)); __ BIND(rounds24_loop); __ subw(rscratch2, rscratch2, 1); @@ -4578,6 +5030,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4611,11 +5066,17 @@ class StubGenerator: public StubCodeGenerator { // vectors write their first lane back to the keystream buffer, followed // by the second lane from all vectors and so on. address generate_chacha20Block_blockpar() { + StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_twoRounds, L_cc20_const; __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_chacha20Block_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); int i, j; @@ -4770,6 +5231,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int64(0x0605040702010003UL); __ emit_int64(0x0E0D0C0F0A09080BUL); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5258,11 +5722,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (short[256]) = c_rarg0 // ntt_zetas (short[256]) = c_rarg1 address generate_kyberNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -5486,6 +5955,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5496,11 +5968,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (short[256]) = c_rarg0 // ntt_zetas (short[256]) = c_rarg1 address generate_kyberInverseNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -5770,6 +6247,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5783,11 +6263,16 @@ class StubGenerator: public StubCodeGenerator { // nttb (short[256]) = c_rarg2 // zetas (short[128]) = c_rarg3 address generate_kyberNttMult() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -5889,6 +6374,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5900,11 +6388,16 @@ class StubGenerator: public StubCodeGenerator { // a (short[256]) = c_rarg1 // b (short[256]) = c_rarg2 address generate_kyberAddPoly_2() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_2_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -5973,6 +6466,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -5985,11 +6481,16 @@ class StubGenerator: public StubCodeGenerator { // b (short[256]) = c_rarg2 // c (short[256]) = c_rarg3 address generate_kyberAddPoly_3() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_3_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -6072,6 +6573,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6092,12 +6596,18 @@ class StubGenerator: public StubCodeGenerator { // parsed (short[]) = c_rarg2 // parsedLength = c_rarg3 address generate_kyber12To16() { + StubId stub_id = StubId::stubgen_kyber12To16_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label L_F00, L_loop; __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_kyber12To16_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register condensed = c_rarg0; @@ -6225,6 +6735,9 @@ class StubGenerator: public StubCodeGenerator { __ emit_int64(0x0f000f000f000f00); __ emit_int64(0x0f000f000f000f00); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6234,11 +6747,16 @@ class StubGenerator: public StubCodeGenerator { // // coeffs (short[256]) = c_rarg0 address generate_kyberBarrettReduce() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberBarrettReduce_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6318,6 +6836,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6481,11 +7002,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // zetas (int[256]) = c_rarg1 address generate_dilithiumAlmostNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6596,6 +7122,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6688,11 +7217,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // zetas (int[256]) = c_rarg1 address generate_dilithiumAlmostInverseNtt() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -6788,6 +7322,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6801,11 +7338,16 @@ class StubGenerator: public StubCodeGenerator { // poly1 (int[256]) = c_rarg1 // poly2 (int[256]) = c_rarg2 address generate_dilithiumNttMult() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -6854,6 +7396,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6865,11 +7410,16 @@ class StubGenerator: public StubCodeGenerator { // coeffs (int[256]) = c_rarg0 // constant (int) = c_rarg1 address generate_dilithiumMontMulByConstant() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumMontMulByConstant_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -6915,6 +7465,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -6929,11 +7482,16 @@ class StubGenerator: public StubCodeGenerator { // twoGamma2 (int) = c_rarg3 // multiplier (int) = c_rarg4 address generate_dilithiumDecomposePoly() { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumDecomposePoly_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_loop; const Register input = c_rarg0; @@ -7073,6 +7631,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(r0, zr); // return 0 __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7212,21 +7773,15 @@ class StubGenerator: public StubCodeGenerator { default: ShouldNotReachHere(); } - - static const uint64_t round_consts[24] = { - 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, - 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008AL, - 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, 0x8000000000008089L, - 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L, - 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L - }; - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -7378,7 +7933,7 @@ class StubGenerator: public StubCodeGenerator { __ fmovs(v1, 1.0); // exact representation __ str(buf, Address(sp, 16)); - __ lea(tmp3, ExternalAddress((address) round_consts)); + __ lea(tmp3, ExternalAddress((address) _sha3_round_consts)); __ BIND(loop_body); keccak_round_gpr(can_use_fp, can_use_r18, tmp3, @@ -7433,6 +7988,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7449,12 +8007,17 @@ class StubGenerator: public StubCodeGenerator { */ address generate_updateBytesCRC32() { assert(UseCRC32Intrinsics, "what are we doing here?"); - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register crc = c_rarg0; // crc const Register buf = c_rarg1; // source java byte array address @@ -7474,6 +8037,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7491,12 +8057,17 @@ class StubGenerator: public StubCodeGenerator { */ address generate_updateBytesCRC32C() { assert(UseCRC32CIntrinsics, "what are we doing here?"); - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32C_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register crc = c_rarg0; // crc const Register buf = c_rarg1; // source java byte array address @@ -7516,6 +8087,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7531,10 +8105,16 @@ class StubGenerator: public StubCodeGenerator { * c_rarg0 - int adler result */ address generate_updateBytesAdler32() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_simple_by1_loop, L_nmax, L_nmax_loop, L_by16, L_by16_loop, L_by1_loop, L_do_mod, L_combine, L_by1; @@ -7702,6 +8282,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7753,11 +8336,17 @@ class StubGenerator: public StubCodeGenerator { * c_rarg4 - z address */ address generate_multiplyToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_multiplyToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register x = r0; const Register xlen = r1; const Register y = r2; @@ -7779,6 +8368,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7786,10 +8378,16 @@ class StubGenerator: public StubCodeGenerator { // squareToLen algorithm for sizes 1..127 described in java code works // faster than multiply_to_len on some CPUs and slower on others, but // multiply_to_len shows a bit better overall results - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_squareToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register x = r0; const Register xlen = r1; @@ -7816,15 +8414,25 @@ class StubGenerator: public StubCodeGenerator { __ pop(spilled_regs, sp); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_mulAdd() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_mulAdd_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register out = r0; const Register in = r1; @@ -7838,6 +8446,9 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7851,10 +8462,16 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerRightShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; @@ -7961,6 +8578,9 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -7974,10 +8594,16 @@ class StubGenerator: public StubCodeGenerator { // c_rarg4 - numIter // address generate_bigIntegerLeftShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label ShiftSIMDLoop, ShiftTwoLoop, ShiftThree, ShiftTwo, ShiftOne, Exit; @@ -8072,10 +8698,25 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_count_positives(address &count_positives_long) { + StubId stub_id = StubId::stubgen_count_positives_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + // We have an extra entry for count_positives_long. + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, + "unexpected extra entry count %d", entries.length()); + count_positives_long = entries.at(0); + return start; + } const u1 large_loop_size = 64; const uint64_t UPPER_BIT_MASK=0x8080808080808080; int dcache_line = VM_Version::dcache_line_size(); @@ -8083,8 +8724,6 @@ class StubGenerator: public StubCodeGenerator { Register ary1 = r1, len = r2, result = r0; __ align(CodeEntryAlignment); - - StubId stub_id = StubId::stubgen_count_positives_id; StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8127,6 +8766,7 @@ class StubGenerator: public StubCodeGenerator { const RegSet spilled_regs = RegSet::range(tmp1, tmp5) + tmp6; count_positives_long = __ pc(); // 2nd entry point + entries.append(count_positives_long); __ enter(); @@ -8241,6 +8881,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(result, result, len); __ ret(lr); + // record the stub entry and end plus the extra entry + store_archive_data(stub_id, entry, __ pc(), &entries); + return entry; } @@ -8331,6 +8974,13 @@ class StubGenerator: public StubCodeGenerator { // r3-r5 are reserved temporary registers // Clobbers: v0-v7 when UseSIMDForArrayEquals, rscratch1, rscratch2 address generate_large_array_equals() { + StubId stub_id = StubId::stubgen_large_array_equals_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Register a1 = r1, a2 = r2, result = r0, cnt1 = r10, tmp1 = rscratch1, tmp2 = rscratch2, tmp3 = r3, tmp4 = r4, tmp5 = r5, tmp6 = r11, tmp7 = r12, tmp8 = r13; @@ -8346,7 +8996,6 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_large_array_equals_id; StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8421,6 +9070,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(NOT_EQUAL_NO_POP); __ leave(); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -8429,6 +9082,33 @@ class StubGenerator: public StubCodeGenerator { // cnt = r2 - elements count // Clobbers: v0-v13, rscratch1, rscratch2 address generate_large_arrays_hashcode(BasicType eltype) { + StubId stub_id; + switch (eltype) { + case T_BOOLEAN: + stub_id = StubId::stubgen_large_arrays_hashcode_boolean_id; + break; + case T_BYTE: + stub_id = StubId::stubgen_large_arrays_hashcode_byte_id; + break; + case T_CHAR: + stub_id = StubId::stubgen_large_arrays_hashcode_char_id; + break; + case T_SHORT: + stub_id = StubId::stubgen_large_arrays_hashcode_short_id; + break; + case T_INT: + stub_id = StubId::stubgen_large_arrays_hashcode_int_id; + break; + default: + stub_id = StubId::NO_STUBID; + ShouldNotReachHere(); + }; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } const Register result = r0, ary = r1, cnt = r2; const FloatRegister vdata0 = v3, vdata1 = v2, vdata2 = v1, vdata3 = v0; const FloatRegister vmul0 = v4, vmul1 = v5, vmul2 = v6, vmul3 = v7; @@ -8472,28 +9152,6 @@ class StubGenerator: public StubCodeGenerator { __ align(CodeEntryAlignment); - StubId stub_id; - switch (eltype) { - case T_BOOLEAN: - stub_id = StubId::stubgen_large_arrays_hashcode_boolean_id; - break; - case T_BYTE: - stub_id = StubId::stubgen_large_arrays_hashcode_byte_id; - break; - case T_CHAR: - stub_id = StubId::stubgen_large_arrays_hashcode_char_id; - break; - case T_SHORT: - stub_id = StubId::stubgen_large_arrays_hashcode_short_id; - break; - case T_INT: - stub_id = StubId::stubgen_large_arrays_hashcode_int_id; - break; - default: - stub_id = StubId::NO_STUBID; - ShouldNotReachHere(); - }; - StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -8728,19 +9386,32 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_dsin_dcos(bool isCos) { - __ align(CodeEntryAlignment); StubId stub_id = (isCos ? StubId::stubgen_dcos_id : StubId::stubgen_dsin_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ generate_dsin_dcos(isCos, (address)StubRoutines::aarch64::_npio2_hw, (address)StubRoutines::aarch64::_two_over_pi, (address)StubRoutines::aarch64::_pio2, (address)StubRoutines::aarch64::_dsin_coef, (address)StubRoutines::aarch64::_dcos_coef); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -8784,8 +9455,14 @@ class StubGenerator: public StubCodeGenerator { // r10 = tmp1 // r11 = tmp2 address generate_compare_long_string_different_encoding(bool isLU) { - __ align(CodeEntryAlignment); StubId stub_id = (isLU ? StubId::stubgen_compare_long_string_LU_id : StubId::stubgen_compare_long_string_UL_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Label SMALL_LOOP, TAIL, TAIL_LOAD_16, LOAD_LAST, DIFF1, DIFF2, @@ -8887,20 +9564,34 @@ class StubGenerator: public StubCodeGenerator { __ subw(result, tmp1, rscratch1); __ bind(DONE); __ ret(lr); - return entry; + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + + return entry; } // r0 = input (float16) // v0 = result (float) // v1 = temporary float register address generate_float16ToFloat() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_hf2f_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("Entry:"); __ flt16_to_flt(v0, r0, v1); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -8908,24 +9599,40 @@ class StubGenerator: public StubCodeGenerator { // r0 = result (float16) // v1 = temporary float register address generate_floatToFloat16() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_f2hf_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); BLOCK_COMMENT("Entry:"); __ flt_to_flt16(r0, v0, v1); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } address generate_method_entry_barrier() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_method_entry_barrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label deoptimize_label; - address start = __ pc(); + start = __ pc(); BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler(); @@ -8974,6 +9681,9 @@ class StubGenerator: public StubCodeGenerator { __ mov(sp, rscratch1); __ br(rscratch2); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -8985,8 +9695,14 @@ class StubGenerator: public StubCodeGenerator { // r10 = tmp1 // r11 = tmp2 address generate_compare_long_string_same_encoding(bool isLL) { - __ align(CodeEntryAlignment); StubId stub_id = (isLL ? StubId::stubgen_compare_long_string_LL_id : StubId::stubgen_compare_long_string_UU_id); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, @@ -9094,6 +9810,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(LENGTH_DIFF); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9125,8 +9845,14 @@ class StubGenerator: public StubCodeGenerator { case UU: stub_id = StubId::stubgen_compare_long_string_UU_id; break; default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); address entry = __ pc(); Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, tmp1 = r10, tmp2 = r11; @@ -9161,8 +9887,6 @@ class StubGenerator: public StubCodeGenerator { ShouldNotReachHere(); \ } - StubCodeMark mark(this, stub_id); - __ mov(idx, 0); __ sve_whilelt(pgtmp1, mode == LL ? __ B : __ H, idx, cnt); @@ -9206,6 +9930,10 @@ class StubGenerator: public StubCodeGenerator { __ bind(DONE); __ ret(lr); #undef LOAD_PAIR + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9267,6 +9995,12 @@ class StubGenerator: public StubCodeGenerator { stub_id = StubId::stubgen_string_indexof_linear_uu_id; } } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); @@ -9535,6 +10269,10 @@ class StubGenerator: public StubCodeGenerator { __ BIND(DONE); __ pop(spilled_regs, sp); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9565,8 +10303,14 @@ class StubGenerator: public StubCodeGenerator { // v1 = loaded 8 bytes // Clobbers: r0, r1, r3, rscratch1, rflags, v0-v6 address generate_large_byte_array_inflate() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_large_byte_array_inflate_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); address entry = __ pc(); Label LOOP, LOOP_START, LOOP_PRFM, LOOP_PRFM_START, DONE; @@ -9605,6 +10349,10 @@ class StubGenerator: public StubCodeGenerator { __ br(__ GE, LOOP); __ bind(DONE); __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, entry, __ pc()); + return entry; } @@ -9620,7 +10368,7 @@ class StubGenerator: public StubCodeGenerator { * Output: * Updated state at c_rarg0 */ - address generate_ghash_processBlocks() { + address generate_ghash_processBlocks_small() { // Bafflingly, GCM uses little-endian for the byte order, but // big-endian for the bit order. For example, the polynomial 1 is // represented as the 16-byte string 80 00 00 00 | 12 bytes of 00. @@ -9632,11 +10380,17 @@ class StubGenerator: public StubCodeGenerator { // that) and keep the data in little-endian bit order through the // calculation, bit-reversing the inputs and outputs. - StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + StubId stub_id = StubId::stubgen_ghash_processBlocks_small_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label polynomial; // local data generated at end of stub - __ align(CodeEntryAlignment); - address start = __ pc(); + start = __ pc(); Register state = c_rarg0; Register subkeyH = c_rarg1; @@ -9696,17 +10450,24 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } - address generate_ghash_processBlocks_wide() { - address small = generate_ghash_processBlocks(); - - StubId stub_id = StubId::stubgen_ghash_processBlocks_wide_id; - StubCodeMark mark(this, stub_id); + address generate_ghash_processBlocks(address small) { + StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } Label polynomial; // local data generated after stub __ align(CodeEntryAlignment); - address start = __ pc(); + StubCodeMark mark(this, stub_id); + start = __ pc(); Register state = c_rarg0; Register subkeyH = c_rarg1; @@ -9748,8 +10509,10 @@ class StubGenerator: public StubCodeGenerator { // 128-bit vector __ emit_int64(0x87); - return start; + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void generate_base64_encode_simdround(Register src, Register dst, @@ -9800,26 +10563,16 @@ class StubGenerator: public StubCodeGenerator { */ address generate_base64_encodeBlock() { - static const char toBase64[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - static const char toBase64URL[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_encodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register src = c_rarg0; // source array Register soff = c_rarg1; // source start offset @@ -9839,9 +10592,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, send, soff); // load the codec base address - __ lea(codec, ExternalAddress((address) toBase64)); + __ lea(codec, ExternalAddress((address) _encodeBlock_toBase64)); __ cbz(isURL, ProcessData); - __ lea(codec, ExternalAddress((address) toBase64URL)); + __ lea(codec, ExternalAddress((address) _encodeBlock_toBase64URL)); __ BIND(ProcessData); @@ -9894,6 +10647,9 @@ class StubGenerator: public StubCodeGenerator { __ BIND(Exit); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10015,80 +10771,16 @@ class StubGenerator: public StubCodeGenerator { // on http://0x80.pl/articles/base64-simd-neon.html#encoding-quadwords, in section // titled "Base64 decoding". - // Non-SIMD lookup tables are mostly dumped from fromBase64 array used in java.util.Base64, - // except the trailing character '=' is also treated illegal value in this intrinsic. That - // is java.util.Base64.fromBase64['='] = -2, while fromBase(URL)64ForNoSIMD['='] = 255 here. - static const uint8_t fromBase64ForNoSIMD[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - static const uint8_t fromBase64URLForNoSIMD[256] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, - 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, - 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, - 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - }; - - // A legal value of base64 code is in range [0, 127]. We need two lookups - // with tbl/tbx and combine them to get the decode data. The 1st table vector - // lookup use tbl, out of range indices are set to 0 in destination. The 2nd - // table vector lookup use tbx, out of range indices are unchanged in - // destination. Input [64..126] is mapped to index [65, 127] in second lookup. - // The value of index 64 is set to 0, so that we know that we already get the - // decoded data with the 1st lookup. - static const uint8_t fromBase64ForSIMD[128] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, - 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, - 255u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, - 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, - }; - - static const uint8_t fromBase64URLForSIMD[128] = { - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, - 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, - 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, - 0u, 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, - 14u, 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, - 63u, 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, - 40u, 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, - }; - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_decodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register src = c_rarg0; // source array Register soff = c_rarg1; // source start offset @@ -10115,9 +10807,9 @@ class StubGenerator: public StubCodeGenerator { __ sub(length, send, soff); __ bfm(length, zr, 0, 1); - __ lea(nosimd_codec, ExternalAddress((address) fromBase64ForNoSIMD)); + __ lea(nosimd_codec, ExternalAddress((address) _decodeBlock_fromBase64ForNoSIMD)); __ cbz(isURL, ProcessData); - __ lea(nosimd_codec, ExternalAddress((address) fromBase64URLForNoSIMD)); + __ lea(nosimd_codec, ExternalAddress((address) _decodeBlock_fromBase64URLForNoSIMD)); __ BIND(ProcessData); __ mov(rscratch1, length); @@ -10162,9 +10854,9 @@ class StubGenerator: public StubCodeGenerator { __ cbzw(rscratch1, Exit); __ sub(length, length, 80); - __ lea(simd_codec, ExternalAddress((address) fromBase64ForSIMD)); + __ lea(simd_codec, ExternalAddress((address) _decodeBlock_fromBase64ForSIMD)); __ cbz(isURL, SIMDEnter); - __ lea(simd_codec, ExternalAddress((address) fromBase64URLForSIMD)); + __ lea(simd_codec, ExternalAddress((address) _decodeBlock_fromBase64URLForSIMD)); __ BIND(SIMDEnter); __ ld1(v0, v1, v2, v3, __ T16B, __ post(simd_codec, 64)); @@ -10197,24 +10889,50 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Support for spin waits. address generate_spin_wait() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_spin_wait_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ spin_wait(); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void generate_lookup_secondary_supers_table_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::_lookup_secondary_supers_table_stubs[0] = start; + for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1); + } + return; + } + StubCodeMark mark(this, stub_id); const Register @@ -10229,7 +10947,13 @@ class StubGenerator: public StubCodeGenerator { vtemp = v0; for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { - StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); + address next_entry = __ pc(); + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry; + if (slot == 0) { + start = next_entry; + } else { + entries.append(next_entry); + } Label L_success; __ enter(); __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, @@ -10239,14 +10963,21 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); } + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); } // Slow path implementation for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_slow_path_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - - address start = __ pc(); + start = __ pc(); const Register r_super_klass = r0, // argument r_array_base = r1, // argument @@ -10258,6 +10989,9 @@ class StubGenerator: public StubCodeGenerator { __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, r_bitmap, temp1, result); __ ret(lr); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10397,14 +11131,43 @@ class StubGenerator: public StubCodeGenerator { if (! UseLSE) { return; } - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_atomic_entry_points_id; - StubCodeMark mark(this, stub_id); - address first_entry = __ pc(); + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + aarch64_atomic_fetch_add_4_impl = (aarch64_atomic_stub_t)start; + int idx = 0; + aarch64_atomic_fetch_add_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_fetch_add_4_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_fetch_add_8_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_xchg_4_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_xchg_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_1_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_1_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_relaxed_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_release_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_release_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_4_seq_cst_impl = (aarch64_atomic_stub_t)entries.at(idx++); + aarch64_atomic_cmpxchg_8_seq_cst_impl = (aarch64_atomic_stub_t)entries.at(idx++); + assert(idx == entries.length(), "sanity!"); + return; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + address end; + { // ADD, memory_order_conservative AtomicStubMark mark_fetch_add_4(_masm, &aarch64_atomic_fetch_add_4_impl); gen_ldadd_entry(Assembler::word, memory_order_conservative); + AtomicStubMark mark_fetch_add_8(_masm, &aarch64_atomic_fetch_add_8_impl); gen_ldadd_entry(Assembler::xword, memory_order_conservative); @@ -10412,6 +11175,7 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_fetch_add_4_relaxed (_masm, &aarch64_atomic_fetch_add_4_relaxed_impl); gen_ldadd_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_fetch_add_8_relaxed (_masm, &aarch64_atomic_fetch_add_8_relaxed_impl); gen_ldadd_entry(MacroAssembler::xword, memory_order_relaxed); @@ -10419,14 +11183,17 @@ class StubGenerator: public StubCodeGenerator { // XCHG, memory_order_conservative AtomicStubMark mark_xchg_4(_masm, &aarch64_atomic_xchg_4_impl); gen_swpal_entry(Assembler::word); - AtomicStubMark mark_xchg_8_impl(_masm, &aarch64_atomic_xchg_8_impl); + + AtomicStubMark mark_xchg_8(_masm, &aarch64_atomic_xchg_8_impl); gen_swpal_entry(Assembler::xword); // CAS, memory_order_conservative AtomicStubMark mark_cmpxchg_1(_masm, &aarch64_atomic_cmpxchg_1_impl); gen_cas_entry(MacroAssembler::byte, memory_order_conservative); + AtomicStubMark mark_cmpxchg_4(_masm, &aarch64_atomic_cmpxchg_4_impl); gen_cas_entry(MacroAssembler::word, memory_order_conservative); + AtomicStubMark mark_cmpxchg_8(_masm, &aarch64_atomic_cmpxchg_8_impl); gen_cas_entry(MacroAssembler::xword, memory_order_conservative); @@ -10434,9 +11201,11 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_1_relaxed (_masm, &aarch64_atomic_cmpxchg_1_relaxed_impl); gen_cas_entry(MacroAssembler::byte, memory_order_relaxed); + AtomicStubMark mark_cmpxchg_4_relaxed (_masm, &aarch64_atomic_cmpxchg_4_relaxed_impl); gen_cas_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_cmpxchg_8_relaxed (_masm, &aarch64_atomic_cmpxchg_8_relaxed_impl); gen_cas_entry(MacroAssembler::xword, memory_order_relaxed); @@ -10444,6 +11213,7 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_4_release (_masm, &aarch64_atomic_cmpxchg_4_release_impl); gen_cas_entry(MacroAssembler::word, memory_order_release); + AtomicStubMark mark_cmpxchg_8_release (_masm, &aarch64_atomic_cmpxchg_8_release_impl); gen_cas_entry(MacroAssembler::xword, memory_order_release); @@ -10451,11 +11221,41 @@ class StubGenerator: public StubCodeGenerator { AtomicStubMark mark_cmpxchg_4_seq_cst (_masm, &aarch64_atomic_cmpxchg_4_seq_cst_impl); gen_cas_entry(MacroAssembler::word, memory_order_seq_cst); + AtomicStubMark mark_cmpxchg_8_seq_cst (_masm, &aarch64_atomic_cmpxchg_8_seq_cst_impl); gen_cas_entry(MacroAssembler::xword, memory_order_seq_cst); - ICache::invalidate_range(first_entry, __ pc() - first_entry); + end = __ pc(); + + ICache::invalidate_range(start, end - start); + // exit block to force update of AtomicStubMark targets + } + + assert(start == (address)aarch64_atomic_fetch_add_4_impl, + "atomic stub should be at start of buffer"); + // record the stub start and end plus all the entries saved by the + // AtomicStubMark destructor + entries.append((address)aarch64_atomic_fetch_add_8_impl); + entries.append((address)aarch64_atomic_fetch_add_4_relaxed_impl); + entries.append((address)aarch64_atomic_fetch_add_8_relaxed_impl); + entries.append((address)aarch64_atomic_xchg_4_impl); + entries.append((address)aarch64_atomic_xchg_8_impl); + entries.append((address)aarch64_atomic_cmpxchg_1_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_impl); + entries.append((address)aarch64_atomic_cmpxchg_1_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_relaxed_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_release_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_release_impl); + entries.append((address)aarch64_atomic_cmpxchg_4_seq_cst_impl); + entries.append((address)aarch64_atomic_cmpxchg_8_seq_cst_impl); + + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + + store_archive_data(stub_id, start, end, &entries); } #endif // LINUX @@ -10559,9 +11359,19 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_thaw_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_top); + + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10570,11 +11380,20 @@ class StubGenerator: public StubCodeGenerator { // TODO: will probably need multiple return barriers depending on return type StubId stub_id = StubId::stubgen_cont_returnBarrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10582,19 +11401,34 @@ class StubGenerator: public StubCodeGenerator { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_returnBarrierExc_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); generate_cont_thaw(Continuation::thaw_return_barrier_exception); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } address generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_preempt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ reset_last_Java_frame(true); @@ -10619,6 +11453,9 @@ class StubGenerator: public StubCodeGenerator { __ ldr(rscratch1, Address(rscratch1)); __ br(rscratch1); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10674,10 +11511,16 @@ class StubGenerator: public StubCodeGenerator { // computation. address generate_poly1305_processBlocks() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_poly1305_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label here; __ enter(); RegSet callee_saved = RegSet::range(r19, r28); @@ -10785,14 +11628,23 @@ class StubGenerator: public StubCodeGenerator { __ leave(); __ ret(lr); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } // exception handler for upcall stubs address generate_upcall_stub_exception_handler() { StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Native caller has no idea how to handle exceptions, // so we just crash here. Up to callee to catch exceptions. @@ -10801,6 +11653,9 @@ class StubGenerator: public StubCodeGenerator { __ blr(rscratch1); __ should_not_reach_here(); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -10809,8 +11664,14 @@ class StubGenerator: public StubCodeGenerator { // rmethod = result address generate_upcall_stub_load_target() { StubId stub_id = StubId::stubgen_upcall_stub_load_target_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ resolve_global_jobject(j_rarg0, rscratch1, rscratch2); // Load target method from receiver @@ -10824,6 +11685,9 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); + // record the stub start and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -11223,8 +12087,6 @@ class StubGenerator: public StubCodeGenerator { */ address generate_multiply() { Label argh, nothing; - bind(argh); - stop("MontgomeryMultiply total_allocation must be <= 8192"); align(CodeEntryAlignment); address entry = pc(); @@ -11331,6 +12193,10 @@ class StubGenerator: public StubCodeGenerator { bind(nothing); ret(lr); + // handler for error case + bind(argh); + stop("MontgomeryMultiply total_allocation must be <= 8192"); + return entry; } // In C, approximately: @@ -11434,8 +12300,6 @@ class StubGenerator: public StubCodeGenerator { */ address generate_square() { Label argh; - bind(argh); - stop("MontgomeryMultiply total_allocation must be <= 8192"); align(CodeEntryAlignment); address entry = pc(); @@ -11544,6 +12408,10 @@ class StubGenerator: public StubCodeGenerator { leave(); ret(lr); + // handler for error case + bind(argh); + stop("MontgomeryMultiply total_allocation must be <= 8192"); + return entry; } // In C, approximately: @@ -11798,18 +12666,32 @@ class StubGenerator: public StubCodeGenerator { if (UseMontgomeryMultiplyIntrinsic) { StubId stub_id = StubId::stubgen_montgomeryMultiply_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); - StubRoutines::_montgomeryMultiply = g.generate_multiply(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/false); + start = g.generate_multiply(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomeryMultiply = start; } if (UseMontgomerySquareIntrinsic) { StubId stub_id = StubId::stubgen_montgomerySquare_id; - StubCodeMark mark(this, stub_id); - MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); - // We use generate_multiply() rather than generate_square() - // because it's faster for the sizes of modulus we care about. - StubRoutines::_montgomerySquare = g.generate_multiply(); + address start = load_archive_data(stub_id); + if (start == nullptr) { + // we have to generate it + StubCodeMark mark(this, stub_id); + MontgomeryMultiplyGenerator g(_masm, /*squaring*/true); + // We use generate_multiply() rather than generate_square() + // because it's faster for the sizes of modulus we care about. + start = g.generate_multiply(); + // record the stub start and end + store_archive_data(stub_id, start, _masm->pc()); + } + StubRoutines::_montgomerySquare = start; } #endif // COMPILER2 @@ -11854,7 +12736,8 @@ class StubGenerator: public StubCodeGenerator { } if (UseGHASHIntrinsics) { // StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); - StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks_wide(); + StubRoutines::aarch64::_ghash_processBlocks_small = generate_ghash_processBlocks_small(); + StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(StubRoutines::aarch64::_ghash_processBlocks_small); } if (UseAESIntrinsics && UseGHASHIntrinsics) { StubRoutines::_galoisCounterMode_AESCrypt = generate_galoisCounterMode_AESCrypt(); @@ -11898,7 +12781,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -11920,12 +12803,35 @@ class StubGenerator: public StubCodeGenerator { break; }; } + +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses) { + // external data defined in this file +#define ADD(addr) external_addresses.append((address)addr); + ADD(_sha256_round_consts); + ADD(_sha512_round_consts); + ADD(_sha3_round_consts); + ADD(_double_keccak_round_consts); + ADD(_encodeBlock_toBase64); + ADD(_encodeBlock_toBase64URL); + ADD(_decodeBlock_fromBase64ForNoSIMD); + ADD(_decodeBlock_fromBase64URLForNoSIMD); + ADD(_decodeBlock_fromBase64ForSIMD); + ADD(_decodeBlock_fromBase64URLForSIMD); +#undef ADD + } +#endif // INCLUDE_CDS }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } +#if INCLUDE_CDS +void StubGenerator_init_AOTAddressTable(GrowableArray
& addresses) { + StubGenerator::init_AOTAddressTable(addresses); +} +#endif // INCLUDE_CDS #if defined (LINUX) diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp index 88993818b47..35ec22b0897 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp @@ -413,3 +413,39 @@ ATTRIBUTE_ALIGNED(64) jdouble StubRoutines::aarch64::_pio2[] = { 2.73370053816464559624e-44, // 0x36E3822280000000 2.16741683877804819444e-51, // 0x3569F31D00000000 }; + +#if INCLUDE_CDS +extern void StubGenerator_init_AOTAddressTable(GrowableArray
& addresses); + +void StubRoutines::init_AOTAddressTable() { + ResourceMark rm; + GrowableArray
external_addresses; + // publish static addresses referred to by aarch64 generator + // n.b. we have to use use an extern call here because class + // StubGenerator, which provides the static method that knows how to + // add the relevant addresses, is declared in a source file rather + // than in a separately includeable header. + StubGenerator_init_AOTAddressTable(external_addresses); + // publish external data addresses defined in nested aarch64 class + StubRoutines::aarch64::init_AOTAddressTable(external_addresses); + AOTCodeCache::publish_external_addresses(external_addresses); +} + + +#define ADD(addr) external_addresses.append((address)addr); + +void StubRoutines::aarch64::init_AOTAddressTable(GrowableArray
& external_addresses) { + ADD(_kyberConsts); + ADD(_dilithiumConsts); + // this is added in generic code + // ADD(_crc_table); + ADD(_adler_table); + ADD(_npio2_hw); + ADD(_dsin_coef); + ADD(_dcos_coef); + ADD(_two_over_pi); + ADD(_pio2); +} + +#undef ADD +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp index c35371e1083..f77192a3741 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp @@ -110,6 +110,11 @@ class aarch64 { _completed = true; } +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS + + private: static uint16_t _kyberConsts[]; static uint32_t _dilithiumConsts[]; diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 8ccffac25a8..15af2d5c4e2 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -24,6 +24,7 @@ * */ +#include "logging/log.hpp" #include "pauth_aarch64.hpp" #include "register_aarch64.hpp" #include "runtime/arguments.hpp" @@ -52,6 +53,10 @@ uintptr_t VM_Version::_pac_mask; SpinWait VM_Version::_spin_wait; +bool VM_Version::_cache_dic_enabled; +bool VM_Version::_cache_idc_enabled; +bool VM_Version::_ic_ivau_trapped; + const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr }; static SpinWait get_spin_wait_desc() { @@ -85,6 +90,19 @@ static SpinWait get_spin_wait_desc() { return spin_wait; } +static bool has_neoverse_n1_errata_1542419() { + const int major_rev_num = VM_Version::cpu_variant(); + const int minor_rev_num = VM_Version::cpu_revision(); + // Neoverse N1: 0xd0c + // Erratum 1542419 affects r3p0, r3p1 and r4p0. + // It is fixed in r4p1 and later revisions, which are not affected. + return (VM_Version::cpu_family() == VM_Version::CPU_ARM && + VM_Version::model_is(0xd0c) && + ((major_rev_num == 3 && minor_rev_num == 0) || + (major_rev_num == 3 && minor_rev_num == 1) || + (major_rev_num == 4 && minor_rev_num == 0))); +} + void VM_Version::initialize() { #define SET_CPU_FEATURE_NAME(id, name, bit) \ _features_names[bit] = XSTR(name); @@ -96,9 +114,14 @@ void VM_Version::initialize() { _supports_atomic_getset8 = true; _supports_atomic_getadd8 = true; + _cache_dic_enabled = false; + _cache_idc_enabled = false; + _ic_ivau_trapped = false; + get_os_cpu_info(); + _cpu_features = _features; - int dcache_line = VM_Version::dcache_line_size(); + int dcache_line = dcache_line_size(); // Limit AllocatePrefetchDistance so that it does not exceed the // static constraint of 512 defined in runtime/globals.hpp. @@ -146,7 +169,7 @@ void VM_Version::initialize() { // if dcpop is available publish data cache line flush size via // generic field, otherwise let if default to zero thereby // disabling writeback - if (VM_Version::supports_dcpop()) { + if (supports_dcpop()) { _data_cache_line_flush_size = dcache_line; } } @@ -267,14 +290,24 @@ void VM_Version::initialize() { } } - if (FLAG_IS_DEFAULT(UseCRC32)) { - UseCRC32 = VM_Version::supports_crc32(); + if (supports_sha1() || supports_sha256() || + supports_sha3() || supports_sha512()) { + if (FLAG_IS_DEFAULT(UseSHA)) { + FLAG_SET_DEFAULT(UseSHA, true); + } else if (!UseSHA) { + clear_feature(CPU_SHA1); + clear_feature(CPU_SHA2); + clear_feature(CPU_SHA3); + clear_feature(CPU_SHA512); + } + } else if (UseSHA) { + warning("SHA instructions are not available on this CPU"); + FLAG_SET_DEFAULT(UseSHA, false); } - if (UseCRC32 && !VM_Version::supports_crc32()) { - warning("UseCRC32 specified, but not supported on this CPU"); - FLAG_SET_DEFAULT(UseCRC32, false); - } + CHECK_CPU_FEATURE(supports_crc32, CRC32); + CHECK_CPU_FEATURE(supports_lse, LSE); + CHECK_CPU_FEATURE(supports_aes, AES); if (_cpu == CPU_ARM && model_is_in({ CPU_MODEL_ARM_NEOVERSE_V1, CPU_MODEL_ARM_NEOVERSE_V2, @@ -287,7 +320,7 @@ void VM_Version::initialize() { } } - if (UseCryptoPmullForCRC32 && (!VM_Version::supports_pmull() || !VM_Version::supports_sha3() || !VM_Version::supports_crc32())) { + if (UseCryptoPmullForCRC32 && (!supports_pmull() || !supports_sha3() || !supports_crc32())) { warning("UseCryptoPmullForCRC32 specified, but not supported on this CPU"); FLAG_SET_DEFAULT(UseCryptoPmullForCRC32, false); } @@ -301,48 +334,40 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } - if (VM_Version::supports_lse()) { - if (FLAG_IS_DEFAULT(UseLSE)) - FLAG_SET_DEFAULT(UseLSE, true); - } else { - if (UseLSE) { - warning("UseLSE specified, but not supported on this CPU"); - FLAG_SET_DEFAULT(UseLSE, false); - } - } - - if (VM_Version::supports_aes()) { - UseAES = UseAES || FLAG_IS_DEFAULT(UseAES); - UseAESIntrinsics = - UseAESIntrinsics || (UseAES && FLAG_IS_DEFAULT(UseAESIntrinsics)); - if (UseAESIntrinsics && !UseAES) { - warning("UseAESIntrinsics enabled, but UseAES not, enabling"); - UseAES = true; + if (supports_aes()) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); } if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); } } else { - if (UseAES) { - warning("AES instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseAES, false); - } - if (UseAESIntrinsics) { - warning("AES intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - } - if (UseAESCTRIntrinsics) { - warning("AES/CTR intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + if (!UseAES) { + if (UseAESIntrinsics) { + warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + if (UseAESCTRIntrinsics) { + warning("AES/CTR intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } else if (!cpu_supports_aes()) { + if (UseAESIntrinsics) { + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + if (UseAESCTRIntrinsics) { + warning("AES/CTR intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } } } - if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { UseCRC32Intrinsics = true; } - if (VM_Version::supports_crc32()) { + if (supports_crc32()) { if (FLAG_IS_DEFAULT(UseCRC32CIntrinsics)) { FLAG_SET_DEFAULT(UseCRC32CIntrinsics, true); } @@ -359,17 +384,7 @@ void VM_Version::initialize() { UseMD5Intrinsics = true; } - if (VM_Version::supports_sha1() || VM_Version::supports_sha256() || - VM_Version::supports_sha3() || VM_Version::supports_sha512()) { - if (FLAG_IS_DEFAULT(UseSHA)) { - FLAG_SET_DEFAULT(UseSHA, true); - } - } else if (UseSHA) { - warning("SHA instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseSHA, false); - } - - if (UseSHA && VM_Version::supports_sha1()) { + if (UseSHA && supports_sha1()) { if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { FLAG_SET_DEFAULT(UseSHA1Intrinsics, true); } @@ -378,7 +393,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); } - if (UseSHA && VM_Version::supports_sha256()) { + if (UseSHA && supports_sha256()) { if (FLAG_IS_DEFAULT(UseSHA256Intrinsics)) { FLAG_SET_DEFAULT(UseSHA256Intrinsics, true); } @@ -388,7 +403,7 @@ void VM_Version::initialize() { } if (UseSHA) { - // No need to check VM_Version::supports_sha3(), since a fallback GPR intrinsic implementation is provided. + // No need to check supports_sha3(), since a fallback GPR intrinsic implementation is provided. if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); } @@ -398,7 +413,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (UseSHA3Intrinsics && VM_Version::supports_sha3()) { + if (UseSHA3Intrinsics && supports_sha3()) { // Auto-enable UseSIMDForSHA3Intrinsic on hardware with performance benefit. // Note that the evaluation of SHA3 extension Intrinsics shows better performance // on Apple and Qualcomm silicon but worse performance on Neoverse V1 and N2. @@ -408,12 +423,12 @@ void VM_Version::initialize() { } } } - if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic && !VM_Version::supports_sha3()) { + if (UseSHA3Intrinsics && UseSIMDForSHA3Intrinsic && !supports_sha3()) { warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (UseSHA && VM_Version::supports_sha512()) { + if (UseSHA && supports_sha512()) { if (FLAG_IS_DEFAULT(UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA512Intrinsics, true); } @@ -426,7 +441,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA, false); } - if (VM_Version::supports_pmull()) { + if (supports_pmull()) { if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { FLAG_SET_DEFAULT(UseGHASHIntrinsics, true); } @@ -477,7 +492,7 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseBlockZeroing, true); } if (FLAG_IS_DEFAULT(BlockZeroingLowLimit)) { - FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * VM_Version::zva_length()); + FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * zva_length()); } } else if (UseBlockZeroing) { if (!FLAG_IS_DEFAULT(UseBlockZeroing)) { @@ -486,11 +501,11 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseBlockZeroing, false); } - if (VM_Version::supports_sve2()) { + if (supports_sve2()) { if (FLAG_IS_DEFAULT(UseSVE)) { FLAG_SET_DEFAULT(UseSVE, 2); } - } else if (VM_Version::supports_sve()) { + } else if (supports_sve()) { if (FLAG_IS_DEFAULT(UseSVE)) { FLAG_SET_DEFAULT(UseSVE, 1); } else if (UseSVE > 1) { @@ -541,7 +556,7 @@ void VM_Version::initialize() { // 1) this code has been built with branch-protection and // 2) the CPU/OS supports it #ifdef __ARM_FEATURE_PAC_DEFAULT - if (!VM_Version::supports_paca()) { + if (!supports_paca()) { // Disable PAC to prevent illegal instruction crashes. warning("ROP-protection specified, but not supported on this CPU. Disabling ROP-protection."); } else { @@ -683,6 +698,43 @@ void VM_Version::initialize() { clear_feature(CPU_SVE); } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation) && is_cache_idc_enabled() && is_cache_dic_enabled()) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && has_neoverse_n1_errata_1542419() + && is_cache_idc_enabled() && !is_cache_dic_enabled()) { + if (_ic_ivau_trapped) { + FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, true); + } else { + log_info(os)("IC IVAU is not trapped; disabling NeoverseN1ICacheErratumMitigation"); + FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, false); + } + } + + if (NeoverseN1ICacheErratumMitigation) { + if (!has_neoverse_n1_errata_1542419()) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set for the CPU not having Neoverse N1 errata 1542419"); + } + // If the user explicitly set the flag, verify the trap is active. + if (!FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && !_ic_ivau_trapped) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but IC IVAU is not trapped. " + "The optimization is not safe on this system."); + } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation)) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (!UseSingleICacheInvalidation) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but UseSingleICacheInvalidation is not enabled"); + } + } + + if (UseSingleICacheInvalidation + && (!is_cache_idc_enabled() || (!is_cache_dic_enabled() && !NeoverseN1ICacheErratumMitigation))) { + vm_exit_during_initialization("UseSingleICacheInvalidation is set but neither IDC nor DIC nor NeoverseN1ICacheErratumMitigation is enabled"); + } + // Construct the "features" string stringStream ss(512); ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 378524fe168..30f1a5d86ca 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -58,6 +58,12 @@ class VM_Version : public Abstract_VM_Version { // When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is // implemented as `movi; cpy(imm, merging)`. static constexpr bool _prefer_sve_merging_mode_cpy = true; + static bool _cache_dic_enabled; + static bool _cache_idc_enabled; + + // IC IVAU trap probe for Neoverse N1 erratum 1542419. + // Set by get_os_cpu_info() on Linux via ic_ivau_probe_linux_aarch64.S. + static bool _ic_ivau_trapped; static SpinWait _spin_wait; @@ -193,6 +199,8 @@ class VM_Version : public Abstract_VM_Version { return (features & BIT_MASK(flag)) != 0; } + static bool cpu_supports_aes() { return supports_feature(_cpu_features, CPU_AES); } + static int cpu_family() { return _cpu; } static int cpu_model() { return _model; } static int cpu_model2() { return _model2; } @@ -255,6 +263,10 @@ class VM_Version : public Abstract_VM_Version { return vector_length_in_bytes <= 16; } + static bool is_cache_dic_enabled() { return _cache_dic_enabled; } + static bool is_cache_idc_enabled() { return _cache_idc_enabled; } + static bool is_ic_ivau_trapped() { return _ic_ivau_trapped; } + static void get_cpu_features_name(void* features_buffer, stringStream& ss); // Returns names of features present in features_set1 but not in features_set2 diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp index a36ad3a0c47..a705b15eff5 100644 --- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp @@ -3211,7 +3211,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -3235,8 +3235,8 @@ class StubGenerator: public StubCodeGenerator { } }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } // implementation of internal development flag diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.cpp b/src/hotspot/cpu/arm/stubRoutines_arm.cpp index a4f2b5e1bd9..3ed747ea11a 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.cpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.cpp @@ -39,3 +39,9 @@ STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + +#if INCLUDE_CDS +// nothing to do for arm +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index e48778a8b9f..f528587a8bb 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -5095,7 +5095,7 @@ void generate_lookup_secondary_supers_table_stub() { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -5119,7 +5119,7 @@ void generate_lookup_secondary_supers_table_stub() { } }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp index 914c5a17a19..3b7ee66348a 100644 --- a/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/stubRoutines_ppc_64.cpp @@ -183,3 +183,9 @@ address StubRoutines::ppc::generate_crc_constants(juint reverse_poly) { return consts; } + +#if INCLUDE_CDS +// nothing to do for ppc +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp index 58eb1a55553..8aced227a06 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp @@ -240,35 +240,6 @@ void LIR_Assembler::arraycopy_type_check(Register src, Register src_pos, Registe } } -void LIR_Assembler::arraycopy_assert(Register src, Register dst, Register tmp, ciArrayKlass *default_type, int flags) { - assert(default_type != nullptr, "null default_type!"); - BasicType basic_type = default_type->element_type()->basic_type(); - if (basic_type == T_ARRAY) { basic_type = T_OBJECT; } - if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { - // Sanity check the known type with the incoming class. For the - // primitive case the types must match exactly with src.klass and - // dst.klass each exactly matching the default type. For the - // object array case, if no type check is needed then either the - // dst type is exactly the expected type and the src type is a - // subtype which we can't check or src is the same array as dst - // but not necessarily exactly of type default_type. - Label known_ok, halt; - __ mov_metadata(tmp, default_type->constant_encoding()); - __ encode_klass_not_null(tmp); - - if (basic_type != T_OBJECT) { - __ cmp_klass_compressed(dst, tmp, t0, halt, false); - __ cmp_klass_compressed(src, tmp, t0, known_ok, true); - } else { - __ cmp_klass_compressed(dst, tmp, t0, known_ok, true); - __ beq(src, dst, known_ok); - } - __ bind(halt); - __ stop("incorrect type information in arraycopy"); - __ bind(known_ok); - } -} - void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { ciArrayKlass *default_type = op->expected_type(); Register src = op->src()->as_register(); @@ -299,7 +270,28 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } #ifdef ASSERT - arraycopy_assert(src, dst, tmp, default_type, flags); + if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { + // Sanity check the known type with the incoming class. For the + // primitive case the types must match exactly with src.klass and + // dst.klass each exactly matching the default type. For the + // object array case, if no type check is needed then either the + // dst type is exactly the expected type and the src type is a + // subtype which we can't check or src is the same array as dst + // but not necessarily exactly of type default_type. + Label known_ok, halt; + __ mov_metadata(tmp, default_type->constant_encoding()); + + if (basic_type != T_OBJECT) { + __ cmp_klass_bne(dst, tmp, t0, t1, halt); + __ cmp_klass_beq(src, tmp, t0, t1, known_ok); + } else { + __ cmp_klass_beq(dst, tmp, t0, t1, known_ok); + __ beq(src, dst, known_ok); + } + __ bind(halt); + __ stop("incorrect type information in arraycopy"); + __ bind(known_ok); + } #endif #ifndef PRODUCT diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp index 06a0f248ca6..b5452f3e4cd 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,6 @@ void arraycopy_type_check(Register src, Register src_pos, Register length, Register dst, Register dst_pos, Register tmp, CodeStub *stub, BasicType basic_type, int flags); - void arraycopy_assert(Register src, Register dst, Register tmp, ciArrayKlass *default_type, int flags); void arraycopy_prepare_params(Register src, Register src_pos, Register length, Register dst, Register dst_pos, BasicType basic_type); void arraycopy_checkcast_prepare_params(Register src, Register src_pos, Register length, diff --git a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp index d94bf428fd2..9eb546a1888 100644 --- a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -56,8 +56,10 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d } } -void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register tmp) { - assert_different_registers(obj, tmp); +void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2) { + precond(tmp1 != noreg); + precond(tmp2 != noreg); + assert_different_registers(obj, tmp1, tmp2); BarrierSet* bs = BarrierSet::barrier_set(); assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); @@ -65,17 +67,17 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob assert(CardTable::dirty_card_val() == 0, "must be"); - __ load_byte_map_base(tmp); - __ add(tmp, obj, tmp); + __ load_byte_map_base(tmp1); + __ add(tmp1, obj, tmp1); if (UseCondCardMark) { Label L_already_dirty; - __ lbu(t1, Address(tmp)); - __ beqz(t1, L_already_dirty); - __ sb(zr, Address(tmp)); + __ lbu(tmp2, Address(tmp1)); + __ beqz(tmp2, L_already_dirty); + __ sb(zr, Address(tmp1)); __ bind(L_already_dirty); } else { - __ sb(zr, Address(tmp)); + __ sb(zr, Address(tmp1)); } } @@ -119,10 +121,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS if (needs_post_barrier) { // flatten object address if needed if (!precise || dst.offset() == 0) { - store_check(masm, dst.base(), tmp3); + store_check(masm, dst.base(), tmp1, tmp2); } else { __ la(tmp3, dst); - store_check(masm, tmp3, t0); + store_check(masm, tmp3, tmp1, tmp2); } } } diff --git a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp index 6f6e9065103..1576f0a6dd8 100644 --- a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -31,7 +31,7 @@ class CardTableBarrierSetAssembler: public BarrierSetAssembler { protected: - void store_check(MacroAssembler* masm, Register obj, Register tmp); + void store_check(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2); virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, RegSet saved_regs) {} diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index b0305fa2977..a2b7970f9f6 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -3511,17 +3511,30 @@ void MacroAssembler::orptr(Address adr, RegisterOrConstant src, Register tmp1, R sd(tmp1, adr); } -void MacroAssembler::cmp_klass_compressed(Register oop, Register trial_klass, Register tmp, Label &L, bool equal) { +void MacroAssembler::cmp_klass_beq(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far) { + assert_different_registers(obj, klass, tmp1, tmp2); if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, oop); + load_narrow_klass_compact(tmp1, obj); } else { - lwu(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); + lwu(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } - if (equal) { - beq(trial_klass, tmp, L); + decode_klass_not_null(tmp1, tmp2); + beq(klass, tmp1, L, is_far); +} + +void MacroAssembler::cmp_klass_bne(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far) { + assert_different_registers(obj, klass, tmp1, tmp2); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp1, obj); } else { - bne(trial_klass, tmp, L); + lwu(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } + decode_klass_not_null(tmp1, tmp2); + bne(klass, tmp1, L, is_far); } // Move an oop into a register. @@ -5527,13 +5540,6 @@ void MacroAssembler::decrementw(const Address dst, int32_t value, Register tmp1, sw(tmp1, adr); } -void MacroAssembler::cmpptr(Register src1, const Address &src2, Label& equal, Register tmp) { - assert_different_registers(src1, tmp); - assert(src2.getMode() == Address::literal, "must be applied to a literal address"); - ld(tmp, src2); - beq(src1, tmp, equal); -} - void MacroAssembler::load_method_holder_cld(Register result, Register method) { load_method_holder(result, method); ld(result, Address(result, InstanceKlass::class_loader_data_offset())); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index f5e985c28a2..a51a6aea468 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -198,7 +198,12 @@ class MacroAssembler: public Assembler { void load_klass(Register dst, Register src, Register tmp = t0); void load_narrow_klass_compact(Register dst, Register src); void store_klass(Register dst, Register src, Register tmp = t0); - void cmp_klass_compressed(Register oop, Register trial_klass, Register tmp, Label &L, bool equal); + void cmp_klass_beq(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far = false); + void cmp_klass_bne(Register obj, Register klass, + Register tmp1, Register tmp2, + Label &L, bool is_far = false); void encode_klass_not_null(Register r, Register tmp = t0); void decode_klass_not_null(Register r, Register tmp = t0); @@ -1348,9 +1353,8 @@ class MacroAssembler: public Assembler { void decrement(const Address dst, int64_t value = 1, Register tmp1 = t0, Register tmp2 = t1); void decrementw(const Address dst, int32_t value = 1, Register tmp1 = t0, Register tmp2 = t1); - void cmpptr(Register src1, const Address &src2, Label& equal, Register tmp = t0); - void clinit_barrier(Register klass, Register tmp, Label* L_fast_path = nullptr, Label* L_slow_path = nullptr); + void load_method_holder_cld(Register result, Register method); void load_method_holder(Register holder, Register method); diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp index d770999df96..e80dedf58ed 100644 --- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp +++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp @@ -72,17 +72,22 @@ void MethodHandles::verify_klass(MacroAssembler* _masm, InstanceKlass** klass_addr = vmClasses::klass_addr_at(klass_id); Klass* klass = vmClasses::klass_at(klass_id); Register temp1 = t1; - Register temp2 = t0; // used by MacroAssembler::cmpptr + Register temp2 = t0; Label L_ok, L_bad; BLOCK_COMMENT("verify_klass {"); __ verify_oop(obj); __ beqz(obj, L_bad); + __ push_reg(RegSet::of(temp1, temp2), sp); __ load_klass(temp1, obj, temp2); - __ cmpptr(temp1, ExternalAddress((address) klass_addr), L_ok); + __ ld(temp2, ExternalAddress((address)klass_addr)); + __ beq(temp1, temp2, L_ok); + intptr_t super_check_offset = klass->super_check_offset(); __ ld(temp1, Address(temp1, super_check_offset)); - __ cmpptr(temp1, ExternalAddress((address) klass_addr), L_ok); + __ ld(temp2, ExternalAddress((address)klass_addr)); + __ beq(temp1, temp2, L_ok); + __ pop_reg(RegSet::of(temp1, temp2), sp); __ bind(L_bad); __ stop(error_message); diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 964c6d98e9c..4656b5c0d41 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -7348,7 +7348,7 @@ static const int64_t right_3_bits = right_n_bits(3); } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -7372,6 +7372,6 @@ static const int64_t right_3_bits = right_n_bits(3); } }; // end class declaration -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index 2aac95d71fa..51e31aa3672 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -501,3 +501,9 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] = 0x751997d0UL, 0x00000001UL, 0xccaa009eUL, 0x00000000UL, }; + +#if INCLUDE_CDS +// nothing to do for riscv +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index 2aa365be999..3f16312eb48 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3422,7 +3422,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -3479,6 +3479,6 @@ class StubGenerator: public StubCodeGenerator { }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.cpp b/src/hotspot/cpu/s390/stubRoutines_s390.cpp index 6feb20f9604..3db4995338d 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.cpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.cpp @@ -736,3 +736,9 @@ juint StubRoutines::zarch::_crc32c_table[CRC32_TABLES][CRC32_COLUMN_SIZE] = { } #endif }; + +#if INCLUDE_CDS +// nothing to do for s390 +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 5397a230642..29925e71aaf 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -42,6 +42,7 @@ #include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/threadIdentifier.hpp" #include "utilities/powerOfTwo.hpp" #include "vmreg_x86.inline.hpp" @@ -70,6 +71,17 @@ static jlong *double_signmask_pool = double_quadword(&fp_signmask_pool[2*2], static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], (jlong)UCONST64(0x8000000080000000), (jlong)UCONST64(0x8000000080000000)); static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jlong)UCONST64(0x8000000000000000), (jlong)UCONST64(0x8000000000000000)); +#if INCLUDE_CDS +// publish external addresses defined in this file +void LIR_Assembler::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(float_signmask_pool); + ADD(double_signmask_pool); + ADD(float_signflip_pool); + ADD(double_signflip_pool); +#undef ADD +} +#endif // INCLUDE_CDS NEEDS_CLEANUP // remove this definitions ? const Register SYNC_header = rax; // synchronization header @@ -518,10 +530,23 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod } case T_LONG: { +#if INCLUDE_CDS + if (AOTCodeCache::is_on_for_dump()) { + address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } + } +#endif assert(patch_code == lir_patch_none, "no patching handled here"); #if INCLUDE_CDS if (AOTCodeCache::is_on_for_dump()) { address b = c->as_pointer(); + if (b == (address)ThreadIdentifier::unsafe_offset()) { + __ lea(dest->as_register_lo(), ExternalAddress(b)); + break; + } if (AOTRuntimeConstants::contains(b)) { __ load_aotrc_address(dest->as_register_lo(), b); break; diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp index c4a368b54d8..6f179255e4a 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.hpp @@ -58,4 +58,7 @@ void store_parameter(jobject c, int offset_from_esp_in_words); void store_parameter(Metadata* c, int offset_from_esp_in_words); +#if INCLUDE_CDS + void static init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS #endif // CPU_X86_C1_LIRASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 0ea769dd488..1de147926bb 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -138,6 +138,8 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob __ shrptr(obj, CardTable::card_shift()); Address card_addr; + precond(rscratch != noreg); + assert_different_registers(obj, rscratch); // The calculation for byte_map_base is as follows: // byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift); @@ -161,7 +163,7 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // entry and that entry is not properly handled by the relocation code. AddressLiteral cardtable((address)byte_map_base, relocInfo::none); Address index(noreg, obj, Address::times_1); - card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch1); + card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch); } int dirty = CardTable::dirty_card_val(); diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 47a3dad54e7..c20551b5084 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -31,6 +31,7 @@ #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zBarrierSetRuntime.hpp" #include "gc/z/zThreadLocalData.hpp" +#include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "runtime/jniHandles.hpp" #include "runtime/sharedRuntime.hpp" @@ -1391,10 +1392,13 @@ static uint16_t patch_barrier_relocation_value(int format) { } } -void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { +void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format, bool log) { const int offset = patch_barrier_relocation_offset(format); const uint16_t value = patch_barrier_relocation_value(format); uint8_t* const patch_addr = (uint8_t*)addr + offset; + if (log) { + log_trace(aot, codecache, stubs)("patching address " INTPTR_FORMAT " offset %d value 0x%x", p2i(addr), offset, value); + } if (format == ZBarrierRelocationFormatLoadGoodBeforeShl) { if (VM_Version::supports_apx_f()) { NativeInstruction* instruction = nativeInstruction_at(addr); @@ -1426,6 +1430,74 @@ void ZBarrierSetAssembler::patch_barriers() { #undef __ #define __ masm-> +void ZBarrierSetAssembler::register_reloc_addresses(GrowableArray
&entries, int begin, int count) { + int formats[] = { + ZBarrierRelocationFormatLoadBadAfterTest, + ZBarrierRelocationFormatStoreBadAfterTest, + ZBarrierRelocationFormatStoreGoodAfterOr, + -1 + }; + int format_idx = 0; + int format = formats[format_idx++]; + for (int i = begin; i < begin + count; i++) { + address addr = entries.at(i); + // reloc addresses occur in 3 groups terminated with a nullptr + if (addr == nullptr) { + assert(format_idx < (int)(sizeof(formats) / sizeof(formats[0])), + "too many reloc groups"); + format = formats[format_idx++]; + } else { + switch(format) { + case ZBarrierRelocationFormatLoadBadAfterTest: + _load_bad_relocations.append(addr); + break; + case ZBarrierRelocationFormatStoreBadAfterTest: + _store_bad_relocations.append(addr); + break; + case ZBarrierRelocationFormatStoreGoodAfterOr: + _store_good_relocations.append(addr); + break; + default: + ShouldNotReachHere(); + break; + } + patch_barrier_relocation(addr, format, true); + } + } + assert(format == -1, "unterminated format list"); +} + +void ZBarrierSetAssembler::retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { + assert(start != nullptr, "start address must not be null"); + assert(end != nullptr, "start address must not be null"); + assert(start < end, "stub range must not be empty"); + for (int i = 0; i < _load_bad_relocations.length(); i++) { + address addr = _load_bad_relocations.at(i); + assert(addr != nullptr, "load bad reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); + for (int i = 0; i < _store_bad_relocations.length(); i++) { + address addr = _store_bad_relocations.at(i); + assert(addr != nullptr, "store bad reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); + for (int i = 0; i < _store_good_relocations.length(); i++) { + address addr = _store_good_relocations.at(i); + assert(addr != nullptr, "store good reloc address shoudl not be null!"); + if (start <= addr && addr < end) { + entries.append(addr); + } + } + entries.append(nullptr); +} + + void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error) { // C1 calls verfy_oop in the middle of barriers, before they have been uncolored // and after being colored. Therefore, we must deal with colored oops as well. diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp index e91e2b9ea20..ce0c4769716 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp @@ -189,10 +189,14 @@ class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { Label& slow_path, Label& slow_path_continuation) const; - void patch_barrier_relocation(address addr, int format); + void patch_barrier_relocation(address addr, int format, bool log = false); void patch_barriers(); + void register_reloc_addresses(GrowableArray
&entries, int begin, int count); + + void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries); + void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error); }; diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 356bf8af5c0..5ab3ca339aa 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -385,7 +385,8 @@ void MacroAssembler::warn(const char* msg) { // Windows always allocates space for its register args subq(rsp, frame::arg_reg_save_area_bytes); #endif - lea(c_rarg0, ExternalAddress((address) msg)); + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + lea(c_rarg0, ExternalAddress((address) str)); call(RuntimeAddress(CAST_FROM_FN_PTR(address, warning))); #ifdef _WIN64 @@ -5672,7 +5673,12 @@ void MacroAssembler::encode_and_move_klass_not_null(Register dst, Register src) BLOCK_COMMENT("encode_and_move_klass_not_null {"); assert_different_registers(src, dst); if (CompressedKlassPointers::base() != nullptr) { - movptr(dst, -(intptr_t)CompressedKlassPointers::base()); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + negq(dst); + } else { + movptr(dst, -(intptr_t)CompressedKlassPointers::base()); + } addq(dst, src); } else { movptr(dst, src); @@ -5720,7 +5726,11 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) } else { if (CompressedKlassPointers::shift() <= Address::times_8) { if (CompressedKlassPointers::base() != nullptr) { - movptr(dst, (intptr_t)CompressedKlassPointers::base()); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + } else { + movptr(dst, (intptr_t)CompressedKlassPointers::base()); + } } else { xorq(dst, dst); } @@ -5732,9 +5742,14 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) } } else { if (CompressedKlassPointers::base() != nullptr) { - const intptr_t base_right_shifted = - (intptr_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); - movptr(dst, base_right_shifted); + if (AOTCodeCache::is_on_for_dump()) { + movptr(dst, ExternalAddress(CompressedKlassPointers::base_addr())); + shrq(dst, CompressedKlassPointers::shift()); + } else { + const intptr_t base_right_shifted = + (intptr_t)CompressedKlassPointers::base() >> CompressedKlassPointers::shift(); + movptr(dst, base_right_shifted); + } } else { xorq(dst, dst); } @@ -5811,7 +5826,7 @@ void MacroAssembler::cmp_narrow_klass(Address dst, Klass* k) { void MacroAssembler::reinit_heapbase() { if (UseCompressedOops) { - if (Universe::heap() != nullptr) { + if (Universe::heap() != nullptr && !AOTCodeCache::is_on_for_dump()) { if (CompressedOops::base() == nullptr) { MacroAssembler::xorptr(r12_heapbase, r12_heapbase); } else { diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp index 9f0232075cd..401d5dc22cc 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_sha.cpp @@ -242,7 +242,6 @@ void MacroAssembler::fast_sha256(XMMRegister msg, XMMRegister state0, XMMRegiste Label done_hash, loop0; address K256 = StubRoutines::x86::k256_addr(); - address pshuffle_byte_flip_mask = StubRoutines::x86::pshuffle_byte_flip_mask_addr(); movdqu(state0, Address(state, 0)); movdqu(state1, Address(state, 16)); @@ -253,7 +252,7 @@ void MacroAssembler::fast_sha256(XMMRegister msg, XMMRegister state0, XMMRegiste palignr(state0, state1, 8); pblendw(state1, msgtmp4, 0xF0); - movdqu(shuf_mask, ExternalAddress(pshuffle_byte_flip_mask)); + movdqu(shuf_mask, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); lea(rax, ExternalAddress(K256)); bind(loop0); @@ -661,8 +660,6 @@ void MacroAssembler::sha256_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste compute_size1, compute_size_end1; address K256_W = StubRoutines::x86::k256_W_addr(); - address pshuffle_byte_flip_mask = StubRoutines::x86::pshuffle_byte_flip_mask_addr(); - address pshuffle_byte_flip_mask_addr = nullptr; const XMMRegister& SHUF_00BA = xmm10; // ymm10: shuffle xBxA -> 00BA const XMMRegister& SHUF_DC00 = xmm12; // ymm12: shuffle xDxC -> DC00 @@ -791,10 +788,14 @@ enum { // load g - r10 after it is used as scratch movl(h, Address(CTX, 4*7)); - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] - vmovdqu(SHUF_00BA, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); // [_SHUF_00BA wrt rip] - vmovdqu(SHUF_DC00, ExternalAddress(pshuffle_byte_flip_mask_addr + 64)); // [_SHUF_DC00 wrt rip] + // the three successive pshuffle_byte_flip_mask stub entries should + // be offset by 32 bytes + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr() + 32 == StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr(), "sanity"); + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr() + 64 == StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr(), "sanity"); + + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] + vmovdqu(SHUF_00BA, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr())); // [_SHUF_00BA wrt rip] + vmovdqu(SHUF_DC00, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr())); // [_SHUF_DC00 wrt rip] movl(g, Address(CTX, 4*6)); @@ -953,11 +954,9 @@ bind(only_one_block); // load g - r10 after use as scratch movl(h, Address(CTX, 4*7)); // 0x5be0cd19 - - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] - vmovdqu(SHUF_00BA, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); // [_SHUF_00BA wrt rip] - vmovdqu(SHUF_DC00, ExternalAddress(pshuffle_byte_flip_mask_addr + 64)); // [_SHUF_DC00 wrt rip] + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr())); // [PSHUFFLE_BYTE_FLIP_MASK wrt rip] + vmovdqu(SHUF_00BA, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_00ba_addr())); // [_SHUF_00BA wrt rip] + vmovdqu(SHUF_DC00, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_dc00_addr())); // [_SHUF_DC00 wrt rip] movl(g, Address(CTX, 4*6)); // 0x1f83d9ab @@ -1346,9 +1345,12 @@ void MacroAssembler::sha512_AVX2(XMMRegister msg, XMMRegister state0, XMMRegiste // load g - r10 after it is used as scratch movq(h, Address(CTX, 8 * 7)); - pshuffle_byte_flip_mask_addr = pshuffle_byte_flip_mask_sha512; - vmovdqu(BYTE_FLIP_MASK, ExternalAddress(pshuffle_byte_flip_mask_addr + 0)); // PSHUFFLE_BYTE_FLIP_MASK wrt rip - vmovdqu(YMM_MASK_LO, ExternalAddress(pshuffle_byte_flip_mask_addr + 32)); + // the two successive pshuffle_byte_flip_mask_sha512 stub entries should + // be offset by 32 bytes + assert(StubRoutines::x86::pshuffle_byte_flip_mask_addr_sha512() + 32 == StubRoutines::x86::pshuffle_byte_flip_mask_ymm_lo_addr_sha512(), "sanity"); + + vmovdqu(BYTE_FLIP_MASK, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_addr_sha512())); // PSHUFFLE_BYTE_FLIP_MASK wrt rip + vmovdqu(YMM_MASK_LO, ExternalAddress(StubRoutines::x86::pshuffle_byte_flip_mask_ymm_lo_addr_sha512())); // MASK_YMM_LO wrt rip movq(g, Address(CTX, 8 * 6)); diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index 971c8fd3c44..07a1ab622ed 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -161,6 +161,12 @@ do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ pshuffle_byte_flip_mask_addr, \ pshuffle_byte_flip_mask_addr) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ + pshuffle_byte_flip_mask_00ba_addr, \ + pshuffle_byte_flip_mask_00ba_addr) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask, \ + pshuffle_byte_flip_mask_dc00_addr, \ + pshuffle_byte_flip_mask_dc00_addr) \ /* x86_64 exposes these 3 stubs via a generic entry array */ \ /* other arches use arch-specific entries */ \ /* this really needs rationalising */ \ @@ -171,6 +177,9 @@ do_arch_entry(x86, compiler, pshuffle_byte_flip_mask_sha512, \ pshuffle_byte_flip_mask_addr_sha512, \ pshuffle_byte_flip_mask_addr_sha512) \ + do_arch_entry(x86, compiler, pshuffle_byte_flip_mask_sha512, \ + pshuffle_byte_flip_mask_ymm_lo_addr_sha512, \ + pshuffle_byte_flip_mask_ymm_lo_addr_sha512) \ do_stub(compiler, compress_perm_table32) \ do_arch_entry(x86, compiler, compress_perm_table32, \ compress_perm_table32, compress_perm_table32) \ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index efb0411aa39..40be816fbf0 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -188,8 +188,18 @@ address StubGenerator::generate_call_stub(address& return_address) { (int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, "adjust this code"); StubId stub_id = StubId::stubgen_call_stub_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == 1, "expected 1 extra entry"); + return_address = entries.at(0); + return start; + } + StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_catch_exception()! const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); @@ -298,6 +308,7 @@ address StubGenerator::generate_call_stub(address& return_address) { BLOCK_COMMENT("call_stub_return_address:"); return_address = __ pc(); + entries.append(return_address); // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) @@ -394,6 +405,9 @@ address StubGenerator::generate_call_stub(address& return_address) { __ movdbl(Address(c_rarg0, 0), xmm0); __ jmp(exit); + // record the stub entry and end plus the auxiliary entry + store_archive_data(stub_id, start, __ pc(), &entries); + return start; } @@ -411,8 +425,15 @@ address StubGenerator::generate_call_stub(address& return_address) { address StubGenerator::generate_catch_exception() { StubId stub_id = StubId::stubgen_catch_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // same as in generate_call_stub(): const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); @@ -442,7 +463,9 @@ address StubGenerator::generate_catch_exception() { __ verify_oop(rax); __ movptr(Address(r15_thread, Thread::pending_exception_offset()), rax); - __ lea(rscratch1, ExternalAddress((address)__FILE__)); + // special case -- add file name string to AOT address table + address file = (address)AOTCodeCache::add_C_string(__FILE__); + __ lea(rscratch1, ExternalAddress(file)); __ movptr(Address(r15_thread, Thread::exception_file_offset()), rscratch1); __ movl(Address(r15_thread, Thread::exception_line_offset()), (int) __LINE__); @@ -451,6 +474,9 @@ address StubGenerator::generate_catch_exception() { "_call_stub_return_address must have been generated before"); __ jump(RuntimeAddress(StubRoutines::_call_stub_return_address)); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -467,8 +493,14 @@ address StubGenerator::generate_catch_exception() { address StubGenerator::generate_forward_exception() { StubId stub_id = StubId::stubgen_forward_exception_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Upon entry, the sp points to the return address returning into // Java (interpreted or compiled) code; i.e., the return address @@ -521,6 +553,9 @@ address StubGenerator::generate_forward_exception() { __ verify_oop(rax); __ jmp(rbx); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -531,12 +566,21 @@ address StubGenerator::generate_forward_exception() { // Result: address StubGenerator::generate_orderaccess_fence() { StubId stub_id = StubId::stubgen_fence_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ membar(Assembler::StoreLoad); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -550,8 +594,14 @@ address StubGenerator::generate_orderaccess_fence() { address StubGenerator::generate_verify_mxcsr() { StubId stub_id = StubId::stubgen_verify_mxcsr_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Address mxcsr_save(rsp, 0); @@ -574,15 +624,24 @@ address StubGenerator::generate_verify_mxcsr() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_f2i_fixup() { StubId stub_id = StubId::stubgen_f2i_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 5 * wordSize); // return address + 4 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -613,14 +672,23 @@ address StubGenerator::generate_f2i_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_f2l_fixup() { StubId stub_id = StubId::stubgen_f2l_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 5 * wordSize); // return address + 4 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -651,15 +719,24 @@ address StubGenerator::generate_f2l_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_d2i_fixup() { StubId stub_id = StubId::stubgen_d2i_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 6 * wordSize); // return address + 5 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -699,15 +776,24 @@ address StubGenerator::generate_d2i_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_d2l_fixup() { StubId stub_id = StubId::stubgen_d2l_fixup_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); Address inout(rsp, 6 * wordSize); // return address + 5 saves - address start = __ pc(); + start = __ pc(); Label L; @@ -747,14 +833,23 @@ address StubGenerator::generate_d2l_fixup() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_count_leading_zeros_lut() { - __ align64(); StubId stub_id = StubId::stubgen_vector_count_leading_zeros_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0101010102020304, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); @@ -765,14 +860,23 @@ address StubGenerator::generate_count_leading_zeros_lut() { __ emit_data64(0x0101010102020304, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_popcount_avx_lut() { - __ align64(); StubId stub_id = StubId::stubgen_vector_popcount_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0302020102010100, relocInfo::none); __ emit_data64(0x0403030203020201, relocInfo::none); @@ -783,14 +887,23 @@ address StubGenerator::generate_popcount_avx_lut() { __ emit_data64(0x0302020102010100, relocInfo::none); __ emit_data64(0x0403030203020201, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_iota_indices() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_iota_indices_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(); // B __ emit_data64(0x0706050403020100, relocInfo::none); __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); @@ -845,14 +958,24 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x4014000000000000, relocInfo::none); // 5.0d __ emit_data64(0x4018000000000000, relocInfo::none); // 6.0d __ emit_data64(0x401c000000000000, relocInfo::none); // 7.0d + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_bit_lut() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_bit_lut_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0E060A020C040800, relocInfo::none); __ emit_data64(0x0F070B030D050901, relocInfo::none); @@ -863,14 +986,23 @@ address StubGenerator::generate_vector_reverse_bit_lut() { __ emit_data64(0x0E060A020C040800, relocInfo::none); __ emit_data64(0x0F070B030D050901, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_long() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_long_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0001020304050607, relocInfo::none); __ emit_data64(0x08090A0B0C0D0E0F, relocInfo::none); @@ -881,14 +1013,23 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_long() { __ emit_data64(0x0001020304050607, relocInfo::none); __ emit_data64(0x08090A0B0C0D0E0F, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_int() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_int_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0C0D0E0F08090A0B, relocInfo::none); @@ -899,14 +1040,23 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_int() { __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0C0D0E0F08090A0B, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_reverse_byte_perm_mask_short() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_reverse_byte_perm_mask_short_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0607040502030001, relocInfo::none); __ emit_data64(0x0E0F0C0D0A0B0809, relocInfo::none); @@ -917,31 +1067,52 @@ address StubGenerator::generate_vector_reverse_byte_perm_mask_short() { __ emit_data64(0x0607040502030001, relocInfo::none); __ emit_data64(0x0E0F0C0D0A0B0809, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_byte_shuffle_mask() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_byte_shuffle_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x7070707070707070, relocInfo::none); __ emit_data64(0x7070707070707070, relocInfo::none); __ emit_data64(0xF0F0F0F0F0F0F0F0, relocInfo::none); __ emit_data64(0xF0F0F0F0F0F0F0F0, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_fp_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64( mask, relocInfo::none ); __ emit_data64( mask, relocInfo::none ); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -957,9 +1128,15 @@ address StubGenerator::generate_compress_perm_table(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (esize == 32) { // Loop to generate 256 x 8 int compression permute index table. A row is // accessed using 8 bit index computed using vector mask. An entry in @@ -997,6 +1174,9 @@ address StubGenerator::generate_compress_perm_table(StubId stub_id) { } } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1012,9 +1192,15 @@ address StubGenerator::generate_expand_perm_table(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); if (esize == 32) { // Loop to generate 256 x 8 int expand permute index table. A row is accessed // using 8 bit index computed using vector mask. An entry in a row holds either @@ -1050,13 +1236,22 @@ address StubGenerator::generate_expand_perm_table(StubId stub_id) { } } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); @@ -1067,14 +1262,23 @@ address StubGenerator::generate_vector_mask(StubId stub_id, int64_t mask) { __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_byte_perm_mask() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vector_byte_perm_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0000000000000001, relocInfo::none); __ emit_data64(0x0000000000000003, relocInfo::none); @@ -1085,13 +1289,22 @@ address StubGenerator::generate_vector_byte_perm_mask() { __ emit_data64(0x0000000000000004, relocInfo::none); __ emit_data64(0x0000000000000006, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_vector_fp_mask(StubId stub_id, int64_t mask) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); @@ -1102,6 +1315,9 @@ address StubGenerator::generate_vector_fp_mask(StubId stub_id, int64_t mask) { __ emit_data64(mask, relocInfo::none); __ emit_data64(mask, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1110,9 +1326,15 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx int32_t val4, int32_t val5, int32_t val6, int32_t val7, int32_t val8, int32_t val9, int32_t val10, int32_t val11, int32_t val12, int32_t val13, int32_t val14, int32_t val15) { + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(len != Assembler::AVX_NoVec, "vector len must be specified"); __ emit_data(val0, relocInfo::none, 0); @@ -1135,6 +1357,9 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx __ emit_data(val15, relocInfo::none, 0); } } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1156,8 +1381,14 @@ address StubGenerator::generate_vector_custom_i32(StubId stub_id, Assembler::Avx // * = popped on exit address StubGenerator::generate_verify_oop() { StubId stub_id = StubId::stubgen_verify_oop_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label exit, error; @@ -1235,6 +1466,9 @@ address StubGenerator::generate_verify_oop() { __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug64))); __ hlt(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1350,35 +1584,46 @@ void StubGenerator::restore_argument_regs(BasicType type) { address StubGenerator::generate_data_cache_writeback() { const Register src = c_rarg0; // source address - - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); __ cache_wb(Address(src, 0)); __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_data_cache_writeback_sync() { const Register is_pre = c_rarg0; // pre or post sync - - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_data_cache_writeback_sync_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); // pre wbsync is a no-op // post wbsync translates to an sfence Label skip; - address start = __ pc(); + start = __ pc(); __ enter(); __ cmpl(is_pre, 0); @@ -1388,6 +1633,9 @@ address StubGenerator::generate_data_cache_writeback_sync() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1405,9 +1653,15 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register buf_param = r15; const Address state_param(rsp, 0 * wordSize); @@ -1437,30 +1691,51 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_upper_word_mask() { - __ align64(); StubId stub_id = StubId::stubgen_upper_word_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0xFFFFFFFF00000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_shuffle_byte_flip_mask() { - __ align64(); StubId stub_id = StubId::stubgen_shuffle_byte_flip_mask_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); __ emit_data64(0x0001020304050607, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1478,9 +1753,15 @@ address StubGenerator::generate_sha1_implCompress(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1509,15 +1790,32 @@ address StubGenerator::generate_sha1_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } -address StubGenerator::generate_pshuffle_byte_flip_mask() { - __ align64(); +address StubGenerator::generate_pshuffle_byte_flip_mask(address& entry_00ba, address& entry_dc00) { StubId stub_id = StubId::stubgen_pshuffle_byte_flip_mask_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 3, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + entry_00ba = entries.at(0); + entry_dc00 = entries.at(1); + assert(VM_Version::supports_avx2() == (entry_00ba != nullptr && entry_dc00 != nullptr), + "entries cannot be null when avx2 is enabled"); + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); - + start = __ pc(); + address entry2 = nullptr; + address entry3 = nullptr; __ emit_data64(0x0405060700010203, relocInfo::none); __ emit_data64(0x0c0d0e0f08090a0b, relocInfo::none); @@ -1525,37 +1823,66 @@ address StubGenerator::generate_pshuffle_byte_flip_mask() { __ emit_data64(0x0405060700010203, relocInfo::none); // second copy __ emit_data64(0x0c0d0e0f08090a0b, relocInfo::none); // _SHUF_00BA + entry2 = __ pc(); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); // _SHUF_DC00 + entry3 = __ pc(); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0x0b0a090803020100, relocInfo::none); } + // have to track the 2nd and 3rd entries even if they are null + entry_00ba = entry2; + entries.push(entry_00ba); + entry_dc00 = entry3; + entries.push(entry_dc00); + + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); return start; } //Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. -address StubGenerator::generate_pshuffle_byte_flip_mask_sha512() { - __ align32(); +address StubGenerator::generate_pshuffle_byte_flip_mask_sha512(address& entry_ymm_lo) { StubId stub_id = StubId::stubgen_pshuffle_byte_flip_mask_sha512_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 2, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == entry_count - 1, + "unexpected extra entry count %d", entries.length()); + entry_ymm_lo = entries.at(0); + assert(VM_Version::supports_avx2() == (entry_ymm_lo != nullptr), + "entry cannot be null when avx2 is enabled"); + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); - + start = __ pc(); + address entry2 = nullptr; if (VM_Version::supports_avx2()) { __ emit_data64(0x0001020304050607, relocInfo::none); // PSHUFFLE_BYTE_FLIP_MASK __ emit_data64(0x08090a0b0c0d0e0f, relocInfo::none); __ emit_data64(0x1011121314151617, relocInfo::none); __ emit_data64(0x18191a1b1c1d1e1f, relocInfo::none); + // capture 2nd entry + entry2 = __ pc(); __ emit_data64(0x0000000000000000, relocInfo::none); //MASK_YMM_LO __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); __ emit_data64(0xFFFFFFFFFFFFFFFF, relocInfo::none); } + // have to track the 2nd entry even if it is null + entry_ymm_lo = entry2; + entries.push(entry2); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc(), &entries); return start; } @@ -1575,9 +1902,15 @@ address StubGenerator::generate_sha256_implCompress(StubId stub_id) { ShouldNotReachHere(); } assert(VM_Version::supports_sha() || VM_Version::supports_avx2(), ""); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1612,6 +1945,9 @@ address StubGenerator::generate_sha256_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1629,9 +1965,15 @@ address StubGenerator::generate_sha512_implCompress(StubId stub_id) { } assert(VM_Version::supports_avx2(), ""); assert(VM_Version::supports_bmi2() || VM_Version::supports_sha512(), ""); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Register buf = c_rarg0; Register state = c_rarg1; @@ -1660,14 +2002,23 @@ address StubGenerator::generate_sha512_implCompress(StubId stub_id) { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_shuffle_addr() { - __ align64(); StubId stub_id = StubId::stubgen_shuffle_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -1680,42 +2031,69 @@ address StubGenerator::base64_shuffle_addr() { __ emit_data64(0x2829272825262425, relocInfo::none); __ emit_data64(0x2e2f2d2e2b2c2a2b, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_shuffle_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_shuffle_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x0809070805060405, relocInfo::none); __ emit_data64(0x0e0f0d0e0b0c0a0b, relocInfo::none); __ emit_data64(0x0405030401020001, relocInfo::none); __ emit_data64(0x0a0b090a07080607, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_input_mask_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_input_mask_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0x8000000000000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); __ emit_data64(0x8000000080000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_avx2_lut_addr() { - __ align32(); StubId stub_id = StubId::stubgen_avx2_lut_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align32(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0xfcfcfcfcfcfc4741, relocInfo::none); __ emit_data64(0x0000f0edfcfcfcfc, relocInfo::none); @@ -1728,14 +2106,23 @@ address StubGenerator::base64_avx2_lut_addr() { __ emit_data64(0xfcfcfcfcfcfc4741, relocInfo::none); __ emit_data64(0x000020effcfcfcfc, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_encoding_table_addr() { - __ align64(); StubId stub_id = StubId::stubgen_encoding_table_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); __ emit_data64(0x4847464544434241, relocInfo::none); @@ -1757,6 +2144,9 @@ address StubGenerator::base64_encoding_table_addr() { __ emit_data64(0x333231307a797877, relocInfo::none); __ emit_data64(0x5f2d393837363534, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1766,10 +2156,16 @@ address StubGenerator::base64_encoding_table_addr() { // boolean isURL) { address StubGenerator::generate_base64_encodeBlock() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_encodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); @@ -2144,15 +2540,24 @@ address StubGenerator::generate_base64_encodeBlock() __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // base64 AVX512vbmi tables address StubGenerator::base64_vbmi_lookup_lo_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_lo_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2165,14 +2570,23 @@ address StubGenerator::base64_vbmi_lookup_lo_addr() { __ emit_data64(0x3b3a393837363534, relocInfo::none); __ emit_data64(0x8080808080803d3c, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_hi_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_hi_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2185,13 +2599,22 @@ address StubGenerator::base64_vbmi_lookup_hi_addr() { __ emit_data64(0x302f2e2d2c2b2a29, relocInfo::none); __ emit_data64(0x8080808080333231, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_lo_url_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_lo_base64url_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2204,14 +2627,23 @@ address StubGenerator::base64_vbmi_lookup_lo_url_addr() { __ emit_data64(0x3b3a393837363534, relocInfo::none); __ emit_data64(0x8080808080803d3c, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_lookup_hi_url_addr() { - __ align64(); StubId stub_id = StubId::stubgen_lookup_hi_base64url_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2224,14 +2656,23 @@ address StubGenerator::base64_vbmi_lookup_hi_url_addr() { __ emit_data64(0x302f2e2d2c2b2a29, relocInfo::none); __ emit_data64(0x8080808080333231, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_pack_vec_addr() { - __ align64(); StubId stub_id = StubId::stubgen_pack_vec_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2244,14 +2685,23 @@ address StubGenerator::base64_vbmi_pack_vec_addr() { __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000000, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_0_1_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_0_1_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2264,14 +2714,23 @@ address StubGenerator::base64_vbmi_join_0_1_addr() { __ emit_data64(0x494a444546404142, relocInfo::none); __ emit_data64(0x565051524c4d4e48, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_1_2_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_1_2_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2284,14 +2743,23 @@ address StubGenerator::base64_vbmi_join_1_2_addr() { __ emit_data64(0x5c5d5e58595a5455, relocInfo::none); __ emit_data64(0x696a646566606162, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_vbmi_join_2_3_addr() { - __ align64(); StubId stub_id = StubId::stubgen_join_2_3_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2304,14 +2772,23 @@ address StubGenerator::base64_vbmi_join_2_3_addr() { __ emit_data64(0x767071726c6d6e68, relocInfo::none); __ emit_data64(0x7c7d7e78797a7475, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_AVX2_decode_tables_addr() { - __ align64(); StubId stub_id = StubId::stubgen_avx2_decode_tables_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2339,14 +2816,23 @@ address StubGenerator::base64_AVX2_decode_tables_addr() { // merge multiplier __ emit_data(0x00011000, relocInfo::none, 0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_AVX2_decode_LUT_tables_addr() { - __ align64(); StubId stub_id = StubId::stubgen_avx2_decode_lut_tables_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align64(); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); assert(((unsigned long long)start & 0x3f) == 0, "Alignment problem (0x%08llx)", (unsigned long long)start); @@ -2380,13 +2866,22 @@ address StubGenerator::base64_AVX2_decode_LUT_tables_addr() { __ emit_data64(0x0804080402011010, relocInfo::none); __ emit_data64(0x1010101010101010, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::base64_decoding_table_addr() { StubId stub_id = StubId::stubgen_decoding_table_base64_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ emit_data64(0xffffffffffffffff, relocInfo::none); __ emit_data64(0xffffffffffffffff, relocInfo::none); @@ -2455,6 +2950,9 @@ address StubGenerator::base64_decoding_table_addr() { __ emit_data64(0xffffffffffffffff, relocInfo::none); __ emit_data64(0xffffffffffffffff, relocInfo::none); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2466,10 +2964,16 @@ address StubGenerator::base64_decoding_table_addr() { // Intrinsic function prototype in Base64.java: // private void decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL, isMIME) { address StubGenerator::generate_base64_decodeBlock() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_base64_decodeBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); @@ -2982,6 +3486,9 @@ address StubGenerator::generate_base64_decodeBlock() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3000,11 +3507,17 @@ address StubGenerator::generate_base64_decodeBlock() { address StubGenerator::generate_updateBytesCRC32() { assert(UseCRC32Intrinsics, "need AVX and CLMUL instructions"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3039,6 +3552,9 @@ address StubGenerator::generate_updateBytesCRC32() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3057,10 +3573,16 @@ address StubGenerator::generate_updateBytesCRC32() { */ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { assert(UseCRC32CIntrinsics, "need SSE4_2"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesCRC32C_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); //reg.arg int#0 int#1 int#2 int#3 int#4 int#5 float regs //Windows RCX RDX R8 R9 none none XMM0..XMM3 @@ -3120,6 +3642,9 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3138,10 +3663,16 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) { * rsp+40 - z address */ address StubGenerator::generate_multiplyToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_multiplyToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3179,6 +3710,9 @@ address StubGenerator::generate_multiplyToLen() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3195,10 +3729,16 @@ address StubGenerator::generate_multiplyToLen() { * rax - int >= mismatched index, < 0 bitwise complement of tail */ address StubGenerator::generate_vectorizedMismatch() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_vectorizedMismatch_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); __ enter(); @@ -3232,6 +3772,9 @@ address StubGenerator::generate_vectorizedMismatch() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3247,10 +3790,16 @@ address StubGenerator::generate_vectorizedMismatch() { */ address StubGenerator::generate_squareToLen() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_squareToLen_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx (c_rarg0, c_rarg1, ...) @@ -3279,14 +3828,23 @@ address StubGenerator::generate_squareToLen() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_method_entry_barrier() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_method_entry_barrier_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label deoptimize_label; @@ -3356,6 +3914,9 @@ address StubGenerator::generate_method_entry_barrier() { __ movptr(rsp, Address(rsp, 0)); // new rsp was written in the barrier __ jmp(Address(rsp, -1 * wordSize)); // jmp target should be callers verified_entry_point + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3373,10 +3934,16 @@ address StubGenerator::generate_method_entry_barrier() { * rsp+40 - k */ address StubGenerator::generate_mulAdd() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_mulAdd_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) @@ -3411,14 +3978,23 @@ address StubGenerator::generate_mulAdd() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_bigIntegerRightShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label Shift512Loop, ShiftTwo, ShiftTwoLoop, ShiftOne, Exit; // For Unix, the arguments are as follows: rdi, rsi, rdx, rcx, r8. @@ -3534,6 +4110,9 @@ address StubGenerator::generate_bigIntegerRightShift() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3551,10 +4130,16 @@ address StubGenerator::generate_bigIntegerRightShift() { * rsp40 - numIter */ address StubGenerator::generate_bigIntegerLeftShift() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label Shift512Loop, ShiftTwo, ShiftTwoLoop, ShiftOne, Exit; // For Unix, the arguments are as follows: rdi, rsi, rdx, rcx, r8. @@ -3659,6 +4244,9 @@ address StubGenerator::generate_bigIntegerLeftShift() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3708,9 +4296,15 @@ void StubGenerator::generate_libm_stubs() { */ address StubGenerator::generate_float16ToFloat() { StubId stub_id = StubId::stubgen_hf2f_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); // No need for RuntimeStub frame since it is called only during JIT compilation @@ -3720,6 +4314,9 @@ address StubGenerator::generate_float16ToFloat() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3734,9 +4331,15 @@ address StubGenerator::generate_float16ToFloat() { */ address StubGenerator::generate_floatToFloat16() { StubId stub_id = StubId::stubgen_f2hf_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); // No need for RuntimeStub frame since it is called only during JIT compilation @@ -3746,6 +4349,9 @@ address StubGenerator::generate_floatToFloat16() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3775,8 +4381,14 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // TODO: Handle Valhalla return types. May require generating different return barriers. @@ -3889,6 +4501,9 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) { __ ret(0); } + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3909,8 +4524,14 @@ address StubGenerator::generate_cont_returnBarrier_exception() { address StubGenerator::generate_cont_preempt_stub() { if (!Continuations::enabled()) return nullptr; StubId stub_id = StubId::stubgen_cont_preempt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ reset_last_Java_frame(true); @@ -3934,14 +4555,23 @@ address StubGenerator::generate_cont_preempt_stub() { __ movptr(rscratch1, ExternalAddress(ContinuationEntry::thaw_call_pc_address())); __ jmp(rscratch1); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // exception handler for upcall stubs address StubGenerator::generate_upcall_stub_exception_handler() { StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // native caller has no idea how to handle exceptions // we just crash here. Up to callee to catch exceptions. @@ -3953,6 +4583,9 @@ address StubGenerator::generate_upcall_stub_exception_handler() { __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception))); __ should_not_reach_here(); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -3961,8 +4594,14 @@ address StubGenerator::generate_upcall_stub_exception_handler() { // rbx = result address StubGenerator::generate_upcall_stub_load_target() { StubId stub_id = StubId::stubgen_upcall_stub_load_target_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ resolve_global_jobject(j_rarg0, rscratch1); // Load target method from receiver @@ -3976,11 +4615,27 @@ address StubGenerator::generate_upcall_stub_load_target() { __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } void StubGenerator::generate_lookup_secondary_supers_table_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id; + GrowableArray
entries; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check"); + address start = load_archive_data(stub_id, &entries); + if (start != nullptr) { + assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::_lookup_secondary_supers_table_stubs[0] = start; + for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1); + } + return; + } StubCodeMark mark(this, stub_id); const Register @@ -3989,21 +4644,35 @@ void StubGenerator::generate_lookup_secondary_supers_table_stub() { result = rdi; for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { - StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); + address next_entry = __ pc(); + if (slot == 0) { + start = next_entry; + } else { + entries.append(next_entry); + } + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry; __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, rdx, rcx, rbx, r11, // temps result, slot); __ ret(0); } + + // record the stub entry and end plus all the auxiliary entries + store_archive_data(stub_id, start, __ pc(), &entries); } // Slow path implementation for UseSecondarySupersTable. address StubGenerator::generate_lookup_secondary_supers_table_slow_path_stub() { StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - - address start = __ pc(); + start = __ pc(); const Register r_super_klass = rax, @@ -4025,6 +4694,9 @@ address StubGenerator::generate_lookup_secondary_supers_table_slow_path_stub() { __ movl(result, 0); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4232,6 +4904,8 @@ void StubGenerator::generate_compiler_stubs() { } if (UseSHA256Intrinsics) { + address entry2 = nullptr; + address entry3 = nullptr; StubRoutines::x86::_k256_adr = (address)StubRoutines::x86::_k256; char* dst = (char*)StubRoutines::x86::_k256_W; char* src = (char*)StubRoutines::x86::_k256; @@ -4240,14 +4914,18 @@ void StubGenerator::generate_compiler_stubs() { memcpy(dst + 32 * ii + 16, src + 16 * ii, 16); } StubRoutines::x86::_k256_W_adr = (address)StubRoutines::x86::_k256_W; - StubRoutines::x86::_pshuffle_byte_flip_mask_addr = generate_pshuffle_byte_flip_mask(); + StubRoutines::x86::_pshuffle_byte_flip_mask_addr = generate_pshuffle_byte_flip_mask(entry2, entry3); + StubRoutines::x86::_pshuffle_byte_flip_mask_00ba_addr = entry2; + StubRoutines::x86::_pshuffle_byte_flip_mask_dc00_addr = entry3; StubRoutines::_sha256_implCompress = generate_sha256_implCompress(StubId::stubgen_sha256_implCompress_id); StubRoutines::_sha256_implCompressMB = generate_sha256_implCompress(StubId::stubgen_sha256_implCompressMB_id); } if (UseSHA512Intrinsics) { + address entry2 = nullptr; StubRoutines::x86::_k512_W_addr = (address)StubRoutines::x86::_k512_W; - StubRoutines::x86::_pshuffle_byte_flip_mask_addr_sha512 = generate_pshuffle_byte_flip_mask_sha512(); + StubRoutines::x86::_pshuffle_byte_flip_mask_addr_sha512 = generate_pshuffle_byte_flip_mask_sha512(entry2); + StubRoutines::x86::_pshuffle_byte_flip_mask_ymm_lo_addr_sha512 = entry2; StubRoutines::_sha512_implCompress = generate_sha512_implCompress(StubId::stubgen_sha512_implCompress_id); StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(StubId::stubgen_sha512_implCompressMB_id); } @@ -4325,7 +5003,7 @@ void StubGenerator::generate_compiler_stubs() { #endif // COMPILER2_OR_JVMCI } -StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { +StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -4348,8 +5026,35 @@ StubGenerator::StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerat }; } -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +#if INCLUDE_CDS +// publish addresses of static data defined in this file and in other +// stubgen stub generator files +void StubGenerator::init_AOTAddressTable(GrowableArray
& external_addresses) { + init_AOTAddressTable_adler(external_addresses); + init_AOTAddressTable_aes(external_addresses); + init_AOTAddressTable_cbrt(external_addresses); + init_AOTAddressTable_chacha(external_addresses); + // constants publishes for all of address use by cos and almost all of sin + init_AOTAddressTable_constants(external_addresses); + init_AOTAddressTable_dilithium(external_addresses); + init_AOTAddressTable_exp(external_addresses); + init_AOTAddressTable_fmod(external_addresses); + init_AOTAddressTable_ghash(external_addresses); + init_AOTAddressTable_kyber(external_addresses); + init_AOTAddressTable_log(external_addresses); + init_AOTAddressTable_poly1305(external_addresses); + init_AOTAddressTable_poly_mont(external_addresses); + init_AOTAddressTable_pow(external_addresses); + init_AOTAddressTable_sha3(external_addresses); + init_AOTAddressTable_sin(external_addresses); + init_AOTAddressTable_sinh(external_addresses); + init_AOTAddressTable_tan(external_addresses); + init_AOTAddressTable_tanh(external_addresses); +} +#endif // INCLUDE_CDS + +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) { + StubGenerator g(code, blob_id, stub_data); } #undef __ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 332add6dcd4..05e8384d636 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -303,11 +303,11 @@ class StubGenerator: public StubCodeGenerator { address generate_sha512_implCompress(StubId stub_id); // Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. - address generate_pshuffle_byte_flip_mask_sha512(); + address generate_pshuffle_byte_flip_mask_sha512(address& entry_ymm_lo); address generate_upper_word_mask(); address generate_shuffle_byte_flip_mask(); - address generate_pshuffle_byte_flip_mask(); + address generate_pshuffle_byte_flip_mask(address& entry_00ba, address& entry_dc0); // AES intrinsic stubs @@ -650,8 +650,33 @@ class StubGenerator: public StubCodeGenerator { void generate_compiler_stubs(); void generate_final_stubs(); +#if INCLUDE_CDS + static void init_AOTAddressTable_adler(GrowableArray
& external_addresses); + static void init_AOTAddressTable_aes(GrowableArray
& external_addresses); + static void init_AOTAddressTable_cbrt(GrowableArray
& external_addresses); + static void init_AOTAddressTable_chacha(GrowableArray
& external_addresses); + static void init_AOTAddressTable_constants(GrowableArray
& external_addresses); + static void init_AOTAddressTable_dilithium(GrowableArray
& external_addresses); + static void init_AOTAddressTable_exp(GrowableArray
& external_addresses); + static void init_AOTAddressTable_fmod(GrowableArray
& external_addresses); + static void init_AOTAddressTable_ghash(GrowableArray
& external_addresses); + static void init_AOTAddressTable_kyber(GrowableArray
& external_addresses); + static void init_AOTAddressTable_log(GrowableArray
& external_addresses); + static void init_AOTAddressTable_poly1305(GrowableArray
& external_addresses); + static void init_AOTAddressTable_poly_mont(GrowableArray
& external_addresses); + static void init_AOTAddressTable_pow(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sha3(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sin(GrowableArray
& external_addresses); + static void init_AOTAddressTable_sinh(GrowableArray
& external_addresses); + static void init_AOTAddressTable_tan(GrowableArray
& external_addresses); + static void init_AOTAddressTable_tanh(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS + public: - StubGenerator(CodeBuffer* code, BlobId blob_id); + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data); +#if INCLUDE_CDS + static void init_AOTAddressTable(GrowableArray
& external_addresses); +#endif // INCLUDE_CDS }; #endif // CPU_X86_STUBGENERATOR_X86_64_HPP diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp index 1d3e7afde1d..a9424978e0e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_adler.cpp @@ -67,8 +67,14 @@ address StubGenerator::generate_updateBytesAdler32() { __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_updateBytesAdler32_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // Choose an appropriate LIMIT for inner loop based on the granularity // of intermediate results. For int, LIMIT of 5552 will ensure intermediate @@ -334,7 +340,19 @@ address StubGenerator::generate_updateBytesAdler32() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_adler(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(ADLER32_ASCALE_TABLE); + ADD(ADLER32_SHUF0_TABLE); + ADD(ADLER32_SHUF1_TABLE); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 1fa80c9d967..b95aa5f8818 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -220,7 +220,7 @@ void StubGenerator::generate_aes_stubs() { StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); StubRoutines::_electronicCodeBook_encryptAESCrypt = generate_electronicCodeBook_encryptAESCrypt_Parallel(); StubRoutines::_electronicCodeBook_decryptAESCrypt = generate_electronicCodeBook_decryptAESCrypt_Parallel(); - if (VM_Version::supports_avx2()) { + if (VM_Version::supports_avx2() && VM_Version::supports_clmul()) { StubRoutines::_galoisCounterMode_AESCrypt = generate_avx2_galoisCounterMode_AESCrypt(); } } @@ -250,10 +250,16 @@ void StubGenerator::generate_aes_stubs() { // Output: // rax - number of processed bytes address StubGenerator::generate_galoisCounterMode_AESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register in = c_rarg0; const Register len = c_rarg1; @@ -319,6 +325,9 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -337,10 +346,16 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() { // Output: // rax - number of processed bytes address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register in = c_rarg0; const Register len = c_rarg1; @@ -404,15 +419,24 @@ address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Vector AES Counter implementation address StubGenerator::generate_counterMode_VectorAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -471,6 +495,9 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -498,10 +525,16 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() { // address StubGenerator::generate_counterMode_AESCrypt_Parallel() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -781,15 +814,24 @@ address StubGenerator::generate_counterMode_AESCrypt_Parallel() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { assert(VM_Version::supports_avx512_vaes(), "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1057,6 +1099,9 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1069,11 +1114,17 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { // address StubGenerator::generate_aescrypt_encryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1152,6 +1203,9 @@ address StubGenerator::generate_aescrypt_encryptBlock() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1164,11 +1218,17 @@ address StubGenerator::generate_aescrypt_encryptBlock() { // address StubGenerator::generate_aescrypt_decryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); Label L_doLast; - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1248,6 +1308,9 @@ address StubGenerator::generate_aescrypt_decryptBlock() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1266,10 +1329,16 @@ address StubGenerator::generate_aescrypt_decryptBlock() { // address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_exit, L_key_192_256, L_key_256, L_loopTop_128, L_loopTop_192, L_loopTop_256; const Register from = c_rarg0; // source array address @@ -1398,6 +1467,9 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { __ jcc(Assembler::notEqual, L_loopTop_256); __ jmp(L_exit); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1422,11 +1494,15 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { // address StubGenerator::generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt) { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = is_encrypt ? StubId::stubgen_electronicCodeBook_encryptAESCrypt_id : StubId::stubgen_electronicCodeBook_decryptAESCrypt_id; + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1581,6 +1657,9 @@ __ opc(xmm_result0, reg); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; #undef DoFour @@ -1612,10 +1691,16 @@ address StubGenerator::generate_electronicCodeBook_decryptAESCrypt_Parallel() { // address StubGenerator::generate_cipherBlockChaining_decryptAESCrypt_Parallel() { assert(UseAES, "need AES instructions and misaligned SSE support"); - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1851,14 +1936,23 @@ __ opc(xmm_result3, src_reg); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_electronicCodeBook_encryptAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_electronicCodeBook_encryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1872,14 +1966,23 @@ address StubGenerator::generate_electronicCodeBook_encryptAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } address StubGenerator::generate_electronicCodeBook_decryptAESCrypt() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_electronicCodeBook_decryptAESCrypt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register from = c_rarg0; // source array address const Register to = c_rarg1; // destination array address @@ -1893,6 +1996,9 @@ address StubGenerator::generate_electronicCodeBook_decryptAESCrypt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -4292,3 +4398,27 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_aes(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(key_shuffle_mask_addr()); + ADD(counter_shuffle_mask_addr()); + ADD(counter_mask_linc0_addr()); + ADD(counter_mask_linc1_addr()); + ADD(counter_mask_linc1f_addr()); + ADD(counter_mask_linc2_addr()); + ADD(counter_mask_linc2f_addr()); + ADD(counter_mask_linc4_addr()); + ADD(counter_mask_linc8_addr()); + ADD(counter_mask_linc16_addr()); + ADD(counter_mask_linc32_addr()); + ADD(counter_mask_ones_addr()); + ADD(ghash_polynomial_reduction_addr()); + ADD(ghash_polynomial_two_one_addr()); + ADD(counter_mask_addbe_4444_addr()); + ADD(counter_mask_addbe_1234_addr()); + ADD(counter_mask_add_1234_addr()); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp index 01e004b7b43..5530e5325de 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_arraycopy.cpp @@ -570,10 +570,45 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres default: ShouldNotReachHere(); } + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + // The stub employs one unsafe handler region by default but has two + // when MaxVectorSize == 64 So we may expect 0, 3 or 6 extras. + int handlers_count = (MaxVectorSize == 64 ? 2 : 1); + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_extra_count = (add_handlers ? handlers_count : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/1/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_extra_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 1/2 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, handlers_count); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); const int large_threshold = 2621440; // 2.5 MB @@ -595,6 +630,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -620,7 +656,7 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres int threshold[] = { 4096, 2048, 1024, 512}; // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid // temp1 holds remaining count and temp4 holds running count used to compute @@ -789,10 +825,28 @@ address StubGenerator::generate_disjoint_copy_avx3_masked(StubId stub_id, addres if (MaxVectorSize == 64) { __ BIND(L_copy_large); - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc); + UnsafeMemoryAccessMark umam(this, add_handlers, false, ucme_exit_pc); arraycopy_avx3_large(to, from, temp1, temp2, temp3, temp4, count, xmm1, xmm2, xmm3, xmm4, shift); __ jmp(L_finish); } + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_extra_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -907,10 +961,41 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres default: ShouldNotReachHere(); } - + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/1 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 1 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 1); + } +#if INCLUDE_ZGC + if (add_relocs) { + // register addresses at which ZGC does colour patching + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); bool use64byteVector = (MaxVectorSize > 32) && (CopyAVX3Threshold == 0); @@ -931,6 +1016,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -957,7 +1043,7 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres int threshold[] = { 4096, 2048, 1024, 512}; // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid // temp1 holds remaining count. @@ -1071,6 +1157,23 @@ address StubGenerator::generate_conjoint_copy_avx3_masked(StubId stub_id, addres __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -1385,9 +1488,29 @@ address StubGenerator::generate_disjoint_byte_copy(address* entry) { return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; @@ -1407,6 +1530,7 @@ address StubGenerator::generate_disjoint_byte_copy(address* entry) { if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1476,6 +1600,17 @@ __ BIND(L_exit); copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_BYTE); __ jmp(L_copy_4_bytes); } + + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1503,9 +1638,29 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes; @@ -1520,6 +1675,7 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1586,6 +1742,16 @@ address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, add __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1616,10 +1782,29 @@ address StubGenerator::generate_disjoint_short_copy(address *entry) { return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif - + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes,L_copy_2_bytes,L_exit; @@ -1638,6 +1823,7 @@ address StubGenerator::generate_disjoint_short_copy(address *entry) { if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1701,6 +1887,16 @@ __ BIND(L_exit); __ jmp(L_copy_4_bytes); } + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1708,7 +1904,6 @@ __ BIND(L_exit); address StubGenerator::generate_fill(StubId stub_id) { BasicType t; bool aligned; - switch (stub_id) { case StubId::stubgen_jbyte_fill_id: t = T_BYTE; @@ -1737,10 +1932,27 @@ address StubGenerator::generate_fill(StubId stub_id) { default: ShouldNotReachHere(); } + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + GrowableArray
extras; + bool add_handlers = ((t == T_BYTE) && !aligned); + int handlers_count = (add_handlers ? 1 : 0); + int expected_extras_count = (handlers_count * UnsafeMemoryAccess::COLUMN_COUNT); // 0/1 x UMAM {start,end,handler} + GrowableArray
* extras_ptr = (add_handlers ? &extras : nullptr); + address start = load_archive_data(stub_id, nullptr, extras_ptr); + if (start != nullptr) { + assert(extras.length() == expected_extras_count, + "unexpected handler addresses count %d", extras.length()); + if (add_handlers) { + // restore 1 x UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 1); + } + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); BLOCK_COMMENT("Entry:"); @@ -1753,7 +1965,7 @@ address StubGenerator::generate_fill(StubId stub_id) { { // Add set memory mark to protect against unsafe accesses faulting - UnsafeMemoryAccessMark umam(this, ((t == T_BYTE) && !aligned), true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); __ generate_fill(t, aligned, to, value, r11, rax, xmm0); } @@ -1761,6 +1973,15 @@ address StubGenerator::generate_fill(StubId stub_id) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_extras_count, + "unexpected handler addresses count %d", extras.length()); + // record the stub entry and end + store_archive_data(stub_id, start, end, nullptr, extras_ptr); + return start; } @@ -1788,10 +2009,29 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif - + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (2 * UnsafeMemoryAccess::COLUMN_COUNT); // 2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + address start = load_archive_data(stub_id, entries_ptr, &extras); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); DecoratorSet decorators = IN_HEAP | IS_ARRAY; Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes; @@ -1806,6 +2046,7 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1864,6 +2105,16 @@ address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, ad __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, &extras); + return start; } @@ -1916,10 +2167,42 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + GrowableArray
entries; + GrowableArray
extras; + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_exit; const Register from = rdi; // source array address @@ -1937,6 +2220,7 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -1957,7 +2241,7 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid __ movptr(dword_count, count); __ shrptr(count, 1); // count => qword_count @@ -1969,20 +2253,20 @@ address StubGenerator::generate_disjoint_int_oop_copy(StubId stub_id, address* e __ jmp(L_copy_bytes); // Copy trailing qwords - __ BIND(L_copy_8_bytes); + __ BIND(L_copy_8_bytes); __ movq(rax, Address(end_from, qword_count, Address::times_8, 8)); __ movq(Address(end_to, qword_count, Address::times_8, 8), rax); __ increment(qword_count); __ jcc(Assembler::notZero, L_copy_8_bytes); // Check for and copy trailing dword - __ BIND(L_copy_4_bytes); + __ BIND(L_copy_4_bytes); __ testl(dword_count, 1); // Only byte test since the value is 0 or 1 __ jccb(Assembler::zero, L_exit); __ movl(rax, Address(end_from, 8)); __ movl(Address(end_to, 8), rax); } -__ BIND(L_exit); + __ BIND(L_exit); address ucme_exit_pc = __ pc(); bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count); restore_arg_regs_using_thread(); @@ -1993,12 +2277,30 @@ __ BIND(L_exit); __ ret(0); { - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc); + UnsafeMemoryAccessMark umam(this, add_handlers, false, ucme_exit_pc); // Copy in multi-bytes chunks copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT); __ jmp(L_copy_4_bytes); } + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2047,10 +2349,42 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 6, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2064,7 +2398,8 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no if (entry != nullptr) { *entry = __ pc(); - // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) + entries.append(*entry); + // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2087,7 +2422,7 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no assert_clean_int(count, rax); // Make sure 'count' is clean int. { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // 'from', 'to' and 'count' are now valid __ movptr(dword_count, count); __ shrptr(count, 1); // count => qword_count @@ -2102,7 +2437,7 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no __ jmp(L_copy_bytes); // Copy trailing qwords - __ BIND(L_copy_8_bytes); + __ BIND(L_copy_8_bytes); __ movq(rax, Address(from, qword_count, Address::times_8, -8)); __ movq(Address(to, qword_count, Address::times_8, -8), rax); __ decrement(qword_count); @@ -2120,12 +2455,12 @@ address StubGenerator::generate_conjoint_int_oop_copy(StubId stub_id, address no { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT); } -__ BIND(L_exit); + __ BIND(L_exit); bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count); restore_arg_regs_using_thread(); INC_COUNTER_NP(SharedRuntime::_jint_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free @@ -2134,6 +2469,23 @@ __ BIND(L_exit); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2180,10 +2532,42 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * return generate_disjoint_copy_avx3_masked(stub_id, entry); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2201,6 +2585,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2221,7 +2606,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count); { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy from low to high addresses. Use 'to' as scratch. __ lea(end_from, Address(from, qword_count, Address::times_8, -8)); @@ -2253,7 +2638,7 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG); } @@ -2269,6 +2654,23 @@ address StubGenerator::generate_disjoint_long_oop_copy(StubId stub_id, address * __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (add_relocs) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2313,10 +2715,42 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target); } #endif + bool add_handlers = !is_oop && !aligned; + bool add_relocs = UseZGC && is_oop; + bool add_extras = add_handlers || add_relocs; + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int expected_handler_count = (add_handlers ? 2 : 0) * UnsafeMemoryAccess::COLUMN_COUNT; // 0/2 x UMAM {start,end,handler} + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (add_extras ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected entry count %d", entries.length()); + assert(!add_handlers || extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } + if (add_handlers) { + // restore 2 UMAM {start,end,handler} addresses from extras + register_unsafe_access_handlers(extras, 0, 2); + } +#if INCLUDE_ZGC + // register addresses at which ZGC does colour patching + if (add_relocs) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_copy_bytes, L_copy_8_bytes, L_exit; const Register from = rdi; // source array address @@ -2329,6 +2763,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); // caller can pass a 64-bit byte count here (from Unsafe.copyMemory) BLOCK_COMMENT("Entry:"); } @@ -2350,7 +2785,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count); { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); __ jmp(L_copy_bytes); @@ -2377,7 +2812,7 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n } { // UnsafeMemoryAccess page error: continue after unsafe access - UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true); + UnsafeMemoryAccessMark umam(this, add_handlers, true); // Copy in multi-bytes chunks copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG); @@ -2393,6 +2828,24 @@ address StubGenerator::generate_conjoint_long_oop_copy(StubId stub_id, address n __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + + // retrieve the registered handler addresses + address end = __ pc(); + if (add_handlers) { + retrieve_unsafe_access_handlers(start, end, extras); + } + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if ((UseZGC && is_oop)) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2448,6 +2901,28 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { ShouldNotReachHere(); } + GrowableArray
entries; + GrowableArray
extras; + int expected_entry_count = (entry != nullptr ? 2 : 1); + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == expected_entry_count, "sanity check"); + GrowableArray
* entries_ptr = (entry_count == 1 ? nullptr : &entries); + GrowableArray
* extras_ptr = (UseZGC ? &extras : nullptr); + address start = load_archive_data(stub_id, entries_ptr, extras_ptr); + if (start != nullptr) { + assert(entries.length() == expected_entry_count - 1, + "unexpected addresses count %d", entries.length()); + if (entry != nullptr) { + *entry = entries.at(0); + } +#if INCLUDE_ZGC + if (UseZGC) { + register_reloc_addresses(extras, 0, extras.length()); + } +#endif // INCLUDE_ZGC + return start; + } + Label L_load_element, L_store_element, L_do_card_marks, L_done; // Input registers (after setup_arg_regs) @@ -2477,7 +2952,7 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2502,6 +2977,7 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { // Caller of this entry point must set up the argument registers. if (entry != nullptr) { *entry = __ pc(); + entries.append(*entry); BLOCK_COMMENT("Entry:"); } @@ -2636,6 +3112,16 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + address end = __ pc(); +#if INCLUDE_ZGC + // retrieve addresses at which ZGC does colour patching + if (UseZGC) { + retrieve_reloc_addresses(start, end, extras); + } +#endif // INCLUDE_ZGC + // record the stub entry and end plus the no_push entry + store_archive_data(stub_id, start, end, entries_ptr, extras_ptr); + return start; } @@ -2655,6 +3141,14 @@ address StubGenerator::generate_checkcast_copy(StubId stub_id, address *entry) { address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address short_copy_entry, address int_copy_entry, address long_copy_entry) { + StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + Label L_long_aligned, L_int_aligned, L_short_aligned; // Input registers (before setup_arg_regs) @@ -2666,9 +3160,8 @@ address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address sho const Register bits = rax; // test copy of low bits __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_unsafe_arraycopy_id; StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -2700,6 +3193,9 @@ address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address sho __ shrptr(size, LogBytesPerLong); // size => qword_count __ jump(RuntimeAddress(long_copy_entry)); + // record the stub entry and end plus + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -2801,10 +3297,23 @@ static void do_setmemory_atomic_loop(USM_TYPE type, Register dest, // to an int, short, or byte fill loop. // address StubGenerator::generate_unsafe_setmemory(address unsafe_byte_fill) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_unsafe_setmemory_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + // we expect three set of extra unsafememory access handler entries + GrowableArray
extras; + int expected_handler_count = 3 * UnsafeMemoryAccess::COLUMN_COUNT; + address start = load_archive_data(stub_id, nullptr, &extras); + if (start != nullptr) { + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + register_unsafe_access_handlers(extras, 0, 3); + return start; + } + + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame assert(unsafe_byte_fill != nullptr, "Invalid call"); @@ -2894,6 +3403,16 @@ address StubGenerator::generate_unsafe_setmemory(address unsafe_byte_fill) { __ jump(RuntimeAddress(unsafe_byte_fill)); } + // retrieve the registered handler addresses + address end = __ pc(); + retrieve_unsafe_access_handlers(start, end, extras); + assert(extras.length() == expected_handler_count, + "unexpected handler addresses count %d", extras.length()); + + // record the stub entry and end plus the no_push entry and any + // extra handler addresses + store_archive_data(stub_id, start, end, nullptr, &extras); + return start; } @@ -2950,7 +3469,15 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh address int_copy_entry, address oop_copy_entry, address long_copy_entry, address checkcast_copy_entry) { - Label L_failed, L_failed_0, L_objArray; + StubId stub_id = StubId::stubgen_generic_arraycopy_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + + Label L_failed, L_failed_0, L_skip_failed_0, L_objArray; Label L_copy_shorts, L_copy_ints, L_copy_longs; // Input registers @@ -2966,22 +3493,9 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh const Register rklass_tmp = rdi; // load_klass #endif - { int modulus = CodeEntryAlignment; - int target = modulus - 5; // 5 = sizeof jmp(L_failed) - int advance = target - (__ offset() % modulus); - if (advance < 0) advance += modulus; - if (advance > 0) __ nop(advance); - } - StubId stub_id = StubId::stubgen_generic_arraycopy_id; StubCodeMark mark(this, stub_id); - - // Short-hop target to L_failed. Makes for denser prologue code. - __ BIND(L_failed_0); - __ jmp(L_failed); - assert(__ offset() % CodeEntryAlignment == 0, "no further alignment needed"); - __ align(CodeEntryAlignment); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame @@ -3022,7 +3536,8 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh // if (dst_pos < 0) return -1; __ testl(dst_pos, dst_pos); // dst_pos (32-bits) size_t j4off = __ offset(); - __ jccb(Assembler::negative, L_failed_0); + // skip over the failure trampoline + __ jccb(Assembler::positive, L_skip_failed_0); // The first four tests are very dense code, // but not quite dense enough to put four @@ -3032,6 +3547,13 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh // Make sure of this. guarantee(((j1off ^ j4off) & ~15) != 0, "I$ line of 1st & 4th jumps"); + // Short-hop target to L_failed. Makes for denser prologue code. + __ BIND(L_failed_0); + __ jmp(L_failed); + + // continue here if first 4 checks pass + __ bind(L_skip_failed_0); + // registers used as temp const Register r11_length = r11; // elements count to copy const Register r10_src_klass = r10; // array klass @@ -3254,6 +3776,9 @@ __ BIND(L_failed); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp index 73330dedc0f..4c647b7d9dc 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp @@ -191,8 +191,14 @@ ATTRIBUTE_ALIGNED(4) static const juint _D_table[] = address StubGenerator::generate_libmCbrt() { StubId stub_id = StubId::stubgen_dcbrt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1; Label B1_1, B1_2, B1_4; @@ -335,7 +341,34 @@ address StubGenerator::generate_libmCbrt() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_cbrt(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(_ABS_MASK); + ADD(_SIG_MASK); + ADD(_EXP_MASK); + ADD(_EXP_MSK2); + ADD(_EXP_MSK3); + ADD(_SCALE63); + ADD(_ZERON); + ADD(_INF); + ADD(_NEG_INF); + address coeff_table = (address)_coeff_table; + ADD(coeff_table); + ADD(coeff_table + 16); + ADD(coeff_table + 32); + ADD(coeff_table + 48); + ADD(_rcp_table); + ADD(_cbrt_table); + ADD(_D_table); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp index 7afaf34e031..1fa51cd2f18 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_chacha.cpp @@ -111,10 +111,16 @@ void StubGenerator::generate_chacha_stubs() { /* The 2-block AVX/AVX2-enabled ChaCha20 block function implementation */ address StubGenerator::generate_chacha20Block_avx() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_twoRounds; const Register state = c_rarg0; @@ -295,15 +301,25 @@ address StubGenerator::generate_chacha20Block_avx() { } __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } /* The 4-block AVX512-enabled ChaCha20 block function implementation */ address StubGenerator::generate_chacha20Block_avx512() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_chacha20Block_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_twoRounds; const Register state = c_rarg0; @@ -466,6 +482,10 @@ address StubGenerator::generate_chacha20Block_avx512() { __ vzeroupper(); __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -584,3 +604,13 @@ bVec, } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_chacha(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(CC20_COUNTER_ADD_AVX); + ADD(CC20_COUNTER_ADD_AVX512); + ADD(CC20_LROT_CONSTS); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp index 93fa7e650db..45c13b7b397 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp @@ -233,3 +233,29 @@ ATTRIBUTE_ALIGNED(16) static const juint _Ctable[] = { }; address StubGenerator::Ctable = (address)_Ctable; +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_constants(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + ADD(_ONE); + ADD(_ONEHALF); + ADD(_SIGN_MASK); + ADD(_TWO_POW_55); + ADD(_TWO_POW_M55); + ADD(_SHIFTER); + ADD(_ZERO); + ADD(_SC_1); + ADD(_SC_2); + ADD(_SC_3); + ADD(_SC_4); + ADD(_PI_4); + ADD(((address)_PI_4+8)); + ADD(_PI32INV); + ADD(_NEG_ZERO); + ADD(_P_1); + ADD(_P_2); + ADD(_P_3); + ADD(_PI_INV_TABLE); + ADD(_Ctable); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp index 8cb6ead21fd..8dedd50cd97 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_cos.cpp @@ -174,8 +174,14 @@ address StubGenerator::generate_libmCos() { StubId stub_id = StubId::stubgen_dcos_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -619,6 +625,9 @@ address StubGenerator::generate_libmCos() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp index b9590939468..de8f52a3c05 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp @@ -401,10 +401,16 @@ static void storeXmms(Register destination, int offset, const XMMRegister xmmReg // static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -646,6 +652,9 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -657,10 +666,16 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen, // zetas (int[128*8]) = c_rarg1 static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumAlmostInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -886,6 +901,9 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -900,10 +918,16 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen, static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -972,6 +996,9 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -984,10 +1011,16 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen, static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumMontMulByConstant_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -1059,6 +1092,9 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1073,10 +1109,16 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen, // multiplier (int) = c_rarg4 static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, int vector_len, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_dilithiumDecomposePoly_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); Label L_loop; @@ -1318,6 +1360,9 @@ static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1340,3 +1385,21 @@ void StubGenerator::generate_dilithium_stubs() { generate_dilithiumDecomposePoly_avx(this, vector_len, _masm); } } + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_dilithium(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + // use accessors to correctly identify the relevant addresses + ADD(unshufflePermsAddr(0)); + ADD(unshufflePermsAddr(1)); + ADD(unshufflePermsAddr(2)); + ADD(unshufflePermsAddr(3)); + ADD(unshufflePermsAddr(4)); + ADD(unshufflePermsAddr(5)); + ADD(dilithiumAvx512ConstsAddr(montQInvModRIdx)); + ADD(dilithiumAvx512ConstsAddr(dilithium_qIdx)); + ADD(dilithiumAvx512ConstsAddr(montRSquareModQIdx)); + ADD(dilithiumAvx512ConstsAddr(barrettAddendIdx)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp index 5130fd2c9d2..2ed9858bf0c 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp @@ -166,8 +166,14 @@ ATTRIBUTE_ALIGNED(4) static const juint _INF[] = address StubGenerator::generate_libmExp() { StubId stub_id = StubId::stubgen_dexp_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -381,7 +387,31 @@ address StubGenerator::generate_libmExp() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_exp(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_cv); + ADD(((address)_cv+16)); + ADD(((address)_cv+32)); + ADD(((address)_cv+48)); + ADD(((address)_cv+64)); + ADD(((address)_cv+80)); + ADD(_mmask); + ADD(_bias); + ADD(_Tbl_addr); + ADD(_ALLONES); + ADD(_ebias); + ADD(_XMAX); + ADD(_XMIN); + ADD(_INF); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp index b1eaa4b8031..f73c8ed459e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp @@ -72,13 +72,19 @@ ATTRIBUTE_ALIGNED(32) static const uint64_t CONST_e307[] = { }; address StubGenerator::generate_libmFmod() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_fmod_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // required for proper stackwalking of RuntimeStub frame - if (VM_Version::supports_avx512vlbwdq()) { // AVX512 version + if (VM_Version::supports_avx512vlbwdq() && VM_Version::supports_fma()) { // AVX512 version // Source used to generate the AVX512 fmod assembly below: // @@ -521,7 +527,22 @@ address StubGenerator::generate_libmFmod() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_fmod(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(CONST_NaN); + ADD(CONST_1p260); + ADD(CONST_MAX); + ADD(CONST_INF); + ADD(CONST_e307); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp index 6f05b1ab5e6..557fe623351 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp @@ -80,11 +80,17 @@ void StubGenerator::generate_ghash_stubs() { // Single and multi-block ghash operations. address StubGenerator::generate_ghash_processBlocks() { - __ align(CodeEntryAlignment); - Label L_ghash_loop, L_exit; StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + Label L_ghash_loop, L_exit; + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); const Register state = c_rarg0; const Register subkeyH = c_rarg1; @@ -211,17 +217,25 @@ address StubGenerator::generate_ghash_processBlocks() { __ leave(); __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } // Ghash single and multi block operations using AVX instructions address StubGenerator::generate_avx_ghash_processBlocks() { - __ align(CodeEntryAlignment); - StubId stub_id = StubId::stubgen_ghash_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); // arguments const Register state = c_rarg0; @@ -237,6 +251,9 @@ address StubGenerator::generate_avx_ghash_processBlocks() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -538,3 +555,14 @@ void StubGenerator::generateHtbl_eight_blocks(Register htbl) { } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_ghash(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(GHASH_SHUFFLE_MASK); + ADD(GHASH_LONG_SWAP_MASK); + ADD(GHASH_BYTE_SWAP_MASK); + ADD(GHASH_POLYNOMIAL); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 7d5dee6a5df..347a9b936a8 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -400,10 +400,16 @@ static int xmm29_29[] = {29, 29, 29, 29}; // ntt_zetas (short[256]) = c_rarg1 address generate_kyberNtt_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -487,6 +493,9 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -496,11 +505,16 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen, // ntt_zetas (short[256]) = c_rarg1 address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberInverseNtt_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -610,6 +624,9 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -621,11 +638,16 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen, // zetas (short[128]) = c_rarg3 address generate_kyberNttMult_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberNttMult_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -731,6 +753,9 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -741,11 +766,16 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen, // b (short[256]) = c_rarg2 address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_2_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -776,6 +806,9 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -787,11 +820,16 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen, // c (short[256]) = c_rarg3 address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberAddPoly_3_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register result = c_rarg0; @@ -830,6 +868,9 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -841,11 +882,16 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen, // parsedLength (int) = c_rarg3 address generate_kyber12To16_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyber12To16_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register condensed = c_rarg0; @@ -984,6 +1030,9 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -993,11 +1042,16 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, // coeffs (short[256]) = c_rarg0 address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, MacroAssembler *_masm) { - - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyberBarrettReduce_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); const Register coeffs = c_rarg0; @@ -1021,6 +1075,9 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, __ mov64(rax, 0); // return 0 __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1038,3 +1095,24 @@ void StubGenerator::generate_kyber_stubs() { } } } + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_kyber(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)) + // use accessors to correctly identify the relevant addresses + ADD(kyberAvx512NttPermsAddr()); + ADD(kyberAvx512InverseNttPermsAddr()); + ADD(kyberAvx512_nttMultPermsAddr()); + ADD(kyberAvx512_12To16PermsAddr()); + ADD(kyberAvx512_12To16DupAddr()); + ADD(kyberAvx512_12To16ShiftAddr()); + ADD(kyberAvx512_12To16AndAddr()); + ADD(kyberAvx512ConstsAddr(qOffset)); + ADD(kyberAvx512ConstsAddr(qInvModROffset)); + ADD(kyberAvx512ConstsAddr(dimHalfInverseOffset)); + ADD(kyberAvx512ConstsAddr(barretMultiplierOffset)); + ADD(kyberAvx512ConstsAddr(montRSquareModqOffset)); + ADD(kyberAvx512ConstsAddr(f00Offset)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp index 6b5b4d704e3..8849597c94b 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp @@ -177,8 +177,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _coeff[] = address StubGenerator::generate_libmLog() { StubId stub_id = StubId::stubgen_dlog_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -359,6 +365,9 @@ address StubGenerator::generate_libmLog() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -516,8 +525,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _coeff_log10[] = address StubGenerator::generate_libmLog10() { StubId stub_id = StubId::stubgen_dlog10_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -704,7 +719,32 @@ address StubGenerator::generate_libmLog10() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_log(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_L_tbl); + ADD(_log2); + ADD(((address)_log2+8)); + ADD(_coeff); + ADD(((address)_coeff+16)); + ADD(((address)_coeff+32)); + ADD(_HIGHSIGMASK_log10); + ADD(_LOG10_E); + ADD(((address)_LOG10_E+8)); + ADD(_L_tbl_log10); + ADD(_log2_log10); + ADD(((address)_log2_log10+8)); + ADD(_coeff_log10); + ADD(((address)_coeff_log10+16)); + ADD(((address)_coeff_log10+32)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp index c80b2d16181..1d0e961c82d 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp @@ -909,10 +909,16 @@ void StubGenerator::poly1305_process_blocks_avx512( // After execution, input and length will point at remaining (unprocessed) data // and accumulator will point to the current accumulator value address StubGenerator::generate_poly1305_processBlocks() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_poly1305_processBlocks_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // Save all 'SOE' registers @@ -1028,6 +1034,10 @@ address StubGenerator::generate_poly1305_processBlocks() { __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -1695,3 +1705,14 @@ void StubGenerator::poly1305_msg_mul_reduce_vec4_avx2( __ vpaddq(A1, A1, YTMP2, Assembler::AVX_256bit); //Add medium 42-bit bits from new blocks to accumulator __ vpaddq(A1, A1, YTMP5, Assembler::AVX_256bit); } +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_poly1305(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(POLY1305_PAD_MSG); + ADD(POLY1305_MASK42); + ADD(POLY1305_MASK44); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp index c439e0b370f..4648fe03aa0 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp @@ -558,10 +558,16 @@ void montgomeryMultiplyAVX2(const Register aLimbs, const Register bLimbs, const } address StubGenerator::generate_intpoly_montgomeryMult_P256() { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_intpoly_montgomeryMult_P256_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); if (VM_Version::supports_avx512ifma() && VM_Version::supports_avx512vlbw()) { @@ -620,6 +626,10 @@ address StubGenerator::generate_intpoly_montgomeryMult_P256() { __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } @@ -680,10 +690,16 @@ address StubGenerator::generate_intpoly_assign() { // P521OrderField: 19 = 8 + 8 + 2 + 1 // Special Cases 5, 10, 14, 16, 19 - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_intpoly_assign_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); __ enter(); // Inputs @@ -762,5 +778,24 @@ address StubGenerator::generate_intpoly_assign() { __ bind(L_Done); __ leave(); __ ret(0); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_poly_mont(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + // use accessors to retrieve all correct addresses + ADD(shift_1L()); + ADD(shift_1R()); + ADD(p256_mask52()); + ADD(mask_limb5()); + ADD(modulus_p256()); + ADD(modulus_p256(1)); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp index 3c3df7e6ac4..5ff09e2b377 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp @@ -760,8 +760,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _DOUBLE0DOT5[] = { address StubGenerator::generate_libmPow() { StubId stub_id = StubId::stubgen_dpow_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_2_0_2, L_2TAG_PACKET_3_0_2; Label L_2TAG_PACKET_4_0_2, L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2, L_2TAG_PACKET_7_0_2; @@ -1859,7 +1865,40 @@ address StubGenerator::generate_libmPow() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_pow(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_HIGHSIGMASK); + ADD(_LOG2_E); + ADD(_HIGHMASK_Y); + ADD((address)_HIGHMASK_Y+8); + ADD(_T_exp); + ADD(_e_coeff); + ADD((address)_e_coeff+16); + ADD((address)_e_coeff+32); + ADD(_coeff_h); + ADD((address)_coeff_h+8); + ADD(_HIGHMASK_LOG_X); + ADD(_HALFMASK); + ADD(_coeff_pow); + ADD((address)_coeff_pow+16); + ADD((address)_coeff_pow+32); + ADD((address)_coeff_pow+48); + ADD((address)_coeff_pow+64); + ADD((address)_coeff_pow+80); + ADD(_L_tbl_pow); + ADD(_log2_pow); + ADD(_DOUBLE2); + ADD(_DOUBLE0); + ADD(_DOUBLE0DOT5); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp index f9d876f34f3..075d25dcac8 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp @@ -104,10 +104,15 @@ static address generate_sha3_implCompress(StubId stub_id, default: ShouldNotReachHere(); } - + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); const Register buf = c_rarg0; const Register state = c_rarg1; @@ -316,6 +321,9 @@ static address generate_sha3_implCompress(StubId stub_id, __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -326,10 +334,16 @@ static address generate_sha3_implCompress(StubId stub_id, // Performs two keccak() computations in parallel. The steps of the // two computations are executed interleaved. static address generate_double_keccak(StubGenerator *stubgen, MacroAssembler *_masm) { - __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_double_keccak_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = stubgen->load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); StubCodeMark mark(stubgen, stub_id); - address start = __ pc(); + start = __ pc(); const Register state0 = c_rarg0; const Register state1 = c_rarg1; @@ -495,6 +509,9 @@ static address generate_double_keccak(StubGenerator *stubgen, MacroAssembler *_m __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + stubgen->store_archive_data(stub_id, start, __ pc()); + return start; } @@ -508,3 +525,14 @@ void StubGenerator::generate_sha3_stubs() { generate_sha3_implCompress(StubId::stubgen_sha3_implCompressMB_id, this, _masm); } } + +#undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sha3(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(round_constsAddr()); + ADD(permsAndRotsAddr()); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp index 5290e737581..eaeaea2c566 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp @@ -181,8 +181,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _ALL_ONES[] = address StubGenerator::generate_libmSin() { StubId stub_id = StubId::stubgen_dsin_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -645,7 +651,18 @@ address StubGenerator::generate_libmSin() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sin(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_ALL_ONES); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp index 86e4ac20176..f6e1d241948 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp @@ -290,8 +290,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = address StubGenerator::generate_libmSinh() { StubId stub_id = StubId::stubgen_dsinh_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_2, L_2TAG_PACKET_1_0_2, L_2TAG_PACKET_3_0_2, L_2TAG_PACKET_4_0_2; Label L_2TAG_PACKET_5_0_2, L_2TAG_PACKET_6_0_2; @@ -519,7 +525,32 @@ address StubGenerator::generate_libmSinh() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_sinh(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_L2E); + ADD(_L2E + 8); + ADD(_HALFMASK); + ADD(_Shifter); + ADD(_cv); + ADD(_cv + 16); + ADD(_cv + 32); + ADD(_cv + 48); + ADD(_cv + 64); + ADD(_T2f); + ADD(_T2_neg_f); + ADD(_pv); + ADD(_pv + 16); + ADD(_pv + 32); + ADD(_MASK3); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp index 4f14414652c..3bfa5a7277f 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp @@ -456,8 +456,14 @@ ATTRIBUTE_ALIGNED(8) static const juint _QQ_2_tan[] = address StubGenerator::generate_libmTan() { StubId stub_id = StubId::stubgen_dtan_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1, L_2TAG_PACKET_7_0_1; @@ -1025,7 +1031,33 @@ address StubGenerator::generate_libmTan() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_tan(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_MUL16); + ADD(_sign_mask_tan); + ADD(_PI32INV_tan); + ADD(_P_1_tan); + ADD(_P_2_tan); + ADD(_P_3_tan); + ADD(_Ctable_tan); + ADD(_MASK_35_tan); + ADD(_Q_11_tan); + ADD(_Q_9_tan); + ADD(_Q_7_tan); + ADD(_Q_5_tan); + ADD(_Q_3_tan); + ADD(_PI_4_tan); + ADD(((address)_PI_4_tan+8)); + ADD(_QQ_2_tan); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp index dce4fbfc455..dcf5f3eb824 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp @@ -303,8 +303,14 @@ ATTRIBUTE_ALIGNED(16) static const juint _T2_neg_f[] = address StubGenerator::generate_libmTanh() { StubId stub_id = StubId::stubgen_dtanh_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } StubCodeMark mark(this, stub_id); - address start = __ pc(); + start = __ pc(); Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1; @@ -495,7 +501,32 @@ address StubGenerator::generate_libmTanh() { __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + return start; } #undef __ + +#if INCLUDE_CDS +void StubGenerator::init_AOTAddressTable_tanh(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(_L2E); + ADD(_L2E + 8); + ADD(_HALFMASK); + ADD(_ONEMASK); + ADD(_TWOMASK); + ADD(_Shifter); + ADD(_cv); + ADD(_cv + 16); + ADD(_cv + 32); + ADD(_T2_neg_f); + ADD(_pv); + ADD(_pv + 16); + ADD(_pv + 32); + ADD(_MASK3); + ADD(_RMASK); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index ee9cea08e64..8696180c512 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -28,6 +28,10 @@ #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" #include "crc32c.h" +#include "stubGenerator_x86_64.hpp" +#ifdef COMPILER1 +#include "c1/c1_LIRAssembler.hpp" +#endif // Implementation of the platform-specific part of StubRoutines - for // a description of how to extend it, see the stubRoutines.hpp file. @@ -411,3 +415,46 @@ ATTRIBUTE_ALIGNED(64) const julong StubRoutines::x86::_k512_W[] = 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; + +#if INCLUDE_CDS + +void StubRoutines::init_AOTAddressTable() { + ResourceMark rm; + GrowableArray
external_addresses; + // publish static addresses referred to by main x86 generator and + // auxiliary x86 generators + StubGenerator::init_AOTAddressTable(external_addresses); + // publish external data addresses defined in nested x86 class + StubRoutines::x86::init_AOTAddressTable(external_addresses); +#ifdef COMPILER1 + LIR_Assembler::init_AOTAddressTable(external_addresses); +#endif + AOTCodeCache::publish_external_addresses(external_addresses); +} + +// publish addresses of external data defined in this file which may +// be referenced from stub or code +void StubRoutines::x86::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)addr); + ADD(&_mxcsr_std); + ADD(&_mxcsr_rz); + ADD(crc_by128_masks_addr()); + ADD(crc_by128_masks_addr() + 16); + ADD(crc_by128_masks_addr() + 32); + // this is added in generic code + // ADD(_crc_table); + ADD(crc_by128_masks_avx512_addr()); + ADD(crc_by128_masks_avx512_addr() + 16); + ADD(crc_by128_masks_avx512_addr() + 32); + ADD(_crc_table_avx512); + ADD(_crc32c_table_avx512); + ADD(_shuf_table_crc32_avx512); + // n.b. call accessor for this one to ensure the table is generated + ADD(crc32c_table_addr()); + ADD(_arrays_hashcode_powers_of_31); + ADD(_k256); + ADD(_k256_W); + ADD(_k512_W); +#undef ADD +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index 3654b644131..3c6d75c1d4e 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -112,6 +112,8 @@ class x86 { static address arrays_hashcode_powers_of_31() { return (address)_arrays_hashcode_powers_of_31; } static void generate_CRC32C_table(bool is_pclmulqdq_supported); + + static void init_AOTAddressTable(GrowableArray
& external_addresses); }; #endif // CPU_X86_STUBROUTINES_X86_HPP diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index a77105efbbf..d8f998520d1 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1094,15 +1094,36 @@ void VM_Version::get_processor_features() { } } - // Currently APX support is only enabled for targets supporting AVX512VL feature. - bool apx_supported = os_supports_apx_egprs() && supports_apx_f() && supports_avx512vl(); - if (UseAPX && !apx_supported) { - warning("UseAPX is not supported on this CPU, setting it to false"); + // Currently APX support is only enabled for targets supporting AVX512VL feature. + if (supports_apx_f() && os_supports_apx_egprs() && supports_avx512vl()) { + if (FLAG_IS_DEFAULT(UseAPX)) { + UseAPX = false; // by default UseAPX is false + _features.clear_feature(CPU_APX_F); + } else if (!UseAPX) { + _features.clear_feature(CPU_APX_F); + } + } else if (UseAPX) { + if (!FLAG_IS_DEFAULT(UseAPX)) { + warning("APX is not supported on this CPU, setting it to false)"); + } FLAG_SET_DEFAULT(UseAPX, false); } - if (!UseAPX) { - _features.clear_feature(CPU_APX_F); + CHECK_CPU_FEATURE(supports_clmul, CLMUL); + CHECK_CPU_FEATURE(supports_aes, AES); + CHECK_CPU_FEATURE(supports_fma, FMA); + + if (supports_sha() || (supports_avx2() && supports_bmi2())) { + if (FLAG_IS_DEFAULT(UseSHA)) { + UseSHA = true; + } else if (!UseSHA) { + _features.clear_feature(CPU_SHA); + } + } else if (UseSHA) { + if (!FLAG_IS_DEFAULT(UseSHA)) { + warning("SHA instructions are not available on this CPU"); + } + FLAG_SET_DEFAULT(UseSHA, false); } if (FLAG_IS_DEFAULT(IntelJccErratumMitigation)) { @@ -1152,9 +1173,40 @@ void VM_Version::get_processor_features() { // Use AES instructions if available. if (supports_aes()) { - if (FLAG_IS_DEFAULT(UseAES)) { - FLAG_SET_DEFAULT(UseAES, true); + if (supports_sse3()) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); + } + } else if (UseAESIntrinsics) { + // The AES intrinsic stubs require AES instruction support (of course) + // but also require sse3 mode or higher for instructions it use. + if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("X86 AES intrinsics require SSE3 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + if (!UseAESIntrinsics) { + if (UseAESCTRIntrinsics) { + if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } + } else { + if (supports_sse4_1()) { + if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); + } + } else if (UseAESCTRIntrinsics) { + // The AES-CTR intrinsic stubs require AES instruction support (of course) + // but also require sse4.1 mode or higher for instructions it use. + if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); + } } + } else { if (!UseAES) { if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); @@ -1164,66 +1216,16 @@ void VM_Version::get_processor_features() { warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } else { - if (UseSSE > 2) { - if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { - FLAG_SET_DEFAULT(UseAESIntrinsics, true); - } - } else { - // The AES intrinsic stubs require AES instruction support (of course) - // but also require sse3 mode or higher for instructions it use. - if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("X86 AES intrinsics require SSE3 instructions or higher. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } else if (!cpu_supports_aes()) { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics are not available on this CPU"); } - - // --AES-CTR begins-- - if (!UseAESIntrinsics) { - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } else { - if (supports_sse4_1()) { - if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, true); - } - } else { - // The AES-CTR intrinsic stubs require AES instruction support (of course) - // but also require sse4.1 mode or higher for instructions it use. - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("X86 AES-CTR intrinsics require SSE4.1 instructions or higher. Intrinsics will be disabled."); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("AES-CTR intrinsics are not available on this CPU"); } - // --AES-CTR ends-- - } - } else if (UseAES || UseAESIntrinsics || UseAESCTRIntrinsics) { - if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { - warning("AES instructions are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAES, false); - if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("AES intrinsics are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES-CTR intrinsics are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } - - // Use CLMUL instructions if available. - if (supports_clmul()) { - if (FLAG_IS_DEFAULT(UseCLMUL)) { - UseCLMUL = true; + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } - } else if (UseCLMUL) { - if (!FLAG_IS_DEFAULT(UseCLMUL)) - warning("CLMUL instructions not available on this CPU (AVX may also be required)"); - FLAG_SET_DEFAULT(UseCLMUL, false); } if (UseCLMUL && (UseSSE > 2)) { @@ -1323,32 +1325,10 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseBASE64Intrinsics, false); } - if (supports_fma()) { - if (FLAG_IS_DEFAULT(UseFMA)) { - UseFMA = true; - } - } else if (UseFMA) { - if (!FLAG_IS_DEFAULT(UseFMA)) { - warning("FMA instructions are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseFMA, false); - } - if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { UseMD5Intrinsics = true; } - if (supports_sha() || (supports_avx2() && supports_bmi2())) { - if (FLAG_IS_DEFAULT(UseSHA)) { - UseSHA = true; - } - } else if (UseSHA) { - if (!FLAG_IS_DEFAULT(UseSHA)) { - warning("SHA instructions are not available on this CPU"); - } - FLAG_SET_DEFAULT(UseSHA, false); - } - if (supports_sha() && supports_sse4_1() && UseSHA) { if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) { FLAG_SET_DEFAULT(UseSHA1Intrinsics, true); @@ -1647,7 +1627,7 @@ void VM_Version::get_processor_features() { } #endif // COMPILER2 - if ((supports_sse4_2() && supports_ht()) || supports_avx()) { // Newest Intel cpus + if (is_intel_modern_cpu()) { // Newest Intel cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus } @@ -1883,7 +1863,7 @@ void VM_Version::get_processor_features() { if (is_intel() && is_intel_server_family() && supports_sse3()) { if (FLAG_IS_DEFAULT(AllocatePrefetchLines) && - supports_sse4_2() && supports_ht()) { // Nehalem based cpus + is_intel_modern_cpu()) { // Nehalem based cpus FLAG_SET_DEFAULT(AllocatePrefetchLines, 4); } #ifdef COMPILER2 @@ -3316,7 +3296,7 @@ int VM_Version::allocate_prefetch_distance(bool use_watermark_prefetch) { } } else { // Intel if (supports_sse3() && is_intel_server_family()) { - if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus + if (is_intel_modern_cpu()) { // Nehalem based cpus return 192; } else if (use_watermark_prefetch) { // watermark prefetching on Core return 384; diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index a42558a8023..f721635a02e 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -535,6 +535,10 @@ class VM_Version : public Abstract_VM_Version { static const char* _features_names[]; + static void clear_feature(Feature_Flag feature) { + _features.clear_feature(feature); + } + static void clear_cpu_features() { _features = VM_Features(); _cpu_features = VM_Features(); @@ -930,6 +934,7 @@ class VM_Version : public Abstract_VM_Version { // Feature identification not affected by VM flags // static bool cpu_supports_evex() { return _cpu_features.supports_feature(CPU_AVX512F); } + static bool cpu_supports_aes() { return _cpu_features.supports_feature(CPU_AES); } static bool supports_avx512_simd_sort() { if (supports_avx512dq()) { @@ -958,6 +963,12 @@ class VM_Version : public Abstract_VM_Version { static bool is_intel_darkmont(); + static bool is_intel_modern_cpu() { + precond(is_intel()); // should be called only for intel CPU + // Efficient cores in hybrid CPU may not support hyper-threads. + return (supports_avx() || (supports_sse4_2() && (supports_ht() || supports_hybrid()))); + } + static bool is_intel_tsc_synched_at_init(); static void insert_features_names(VM_Version::VM_Features features, stringStream& ss); diff --git a/src/hotspot/cpu/zero/stubGenerator_zero.cpp b/src/hotspot/cpu/zero/stubGenerator_zero.cpp index 08cb173b507..569a2fa8ca9 100644 --- a/src/hotspot/cpu/zero/stubGenerator_zero.cpp +++ b/src/hotspot/cpu/zero/stubGenerator_zero.cpp @@ -213,7 +213,7 @@ class StubGenerator: public StubCodeGenerator { } public: - StubGenerator(CodeBuffer* code, BlobId blob_id) : StubCodeGenerator(code, blob_id) { + StubGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) : StubCodeGenerator(code, blob_id, stub_data) { switch(blob_id) { case BlobId::stubgen_preuniverse_id: generate_preuniverse_stubs(); @@ -237,8 +237,8 @@ class StubGenerator: public StubCodeGenerator { } }; -void StubGenerator_generate(CodeBuffer* code, BlobId blob_id) { - StubGenerator g(code, blob_id); +void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData *stub_data) { + StubGenerator g(code, blob_id, stub_data); } EntryFrame *EntryFrame::build(const intptr_t* parameters, diff --git a/src/hotspot/cpu/zero/stubRoutines_zero.cpp b/src/hotspot/cpu/zero/stubRoutines_zero.cpp index 9b53f09be5d..196907b061f 100644 --- a/src/hotspot/cpu/zero/stubRoutines_zero.cpp +++ b/src/hotspot/cpu/zero/stubRoutines_zero.cpp @@ -30,3 +30,9 @@ address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; } address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; } + +#if INCLUDE_CDS +// nothing to do for zero +void StubRoutines::init_AOTAddressTable() { +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index b8be77c5e05..c5046797e02 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -701,6 +701,39 @@ static void remove_file(const char* path) { } } +// Files newer than this threshold are considered to belong to a JVM that may +// still be starting up and are therefore not candidates for stale-file +// cleanup. This avoids racing a concurrent JVM startup while scanning the +// hsperfdata directory. +static const time_t cleanup_grace_period_seconds = 5; + +static bool is_cleanup_candidate(const char* filename, const char* dirname) { + struct stat statbuf; + int result; + + RESTARTABLE(::lstat(filename, &statbuf), result); + if (result == OS_ERR) { + log_debug(perf, memops)("lstat failed for %s/%s: %s", dirname, filename, os::strerror(errno)); + return false; + } + + if (!S_ISREG(statbuf.st_mode)) { + return false; + } + + const time_t now = time(nullptr); + if (now == (time_t)-1) { + return false; + } + + if (statbuf.st_mtime >= now - cleanup_grace_period_seconds) { + log_debug(perf, memops)("Skip cleanup of fresh file %s/%s", dirname, filename); + return false; + } + + return true; +} + // cleanup stale shared memory files // // This method attempts to remove all stale shared memory files in @@ -744,6 +777,11 @@ static void cleanup_sharedmem_files(const char* dirname) { continue; } + if (!is_cleanup_candidate(filename, dirname)) { + errno = 0; + continue; + } + #if defined(LINUX) // Special case on Linux, if multiple containers share the // same /tmp directory: @@ -872,16 +910,56 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size return -1; } - // Open the filename in the current directory. - // Cannot use O_TRUNC here; truncation of an existing file has to happen - // after the is_file_secure() check below. - int fd; - RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); + int fd = OS_ERR; + static const int create_sharedmem_file_retry_count = LINUX_ONLY(3) NOT_LINUX(1); + for (int attempt = 0; attempt < create_sharedmem_file_retry_count; attempt++) { + // Open the filename in the current directory. + // Use O_EXCL so that startup never reuses an existing pid file unless it + // has first been proven stale and removed in `cleanup_sharedmem_files`. + RESTARTABLE(os::open(filename, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, S_IRUSR|S_IWUSR), fd); + if (fd == OS_ERR) { + break; + } + +#if defined(LINUX) + // On Linux, different containerized processes that share the same /tmp + // directory (e.g., with "docker --volume ...") may have the same pid and + // try to use the same file. To avoid conflicts among such processes, we + // allow only one of them (the winner of the flock() call) to write to the + // file. If we lose the race, assume we may have collided with a concurrent + // scavenger briefly holding the lock on a fresh file and retry a few times + // before giving up. + int n; + RESTARTABLE(::flock(fd, LOCK_EX|LOCK_NB), n); + if (n == 0) { + break; + } + + const int flock_errno = errno; + ::close(fd); + fd = OS_ERR; + + if (attempt + 1 == create_sharedmem_file_retry_count || flock_errno != EWOULDBLOCK) { + log_warning(perf, memops)("Cannot use file %s/%s because %s (errno = %d)", dirname, filename, + (flock_errno == EWOULDBLOCK) ? + "it is locked by another process" : + "flock() failed", flock_errno); + errno = flock_errno; + break; + } + + // Short sleep to allow the lock to free up. + os::naked_short_sleep(1); +#endif + } + if (fd == OS_ERR) { if (log_is_enabled(Debug, perf)) { LogStreamHandle(Debug, perf) log; if (errno == ELOOP) { log.print_cr("file %s is a symlink and is not secure", filename); + } else if (errno == EEXIST) { + log.print_cr("could not create file %s: existing file is not provably stale", filename); } else { log.print_cr("could not create file %s: %s", filename, os::strerror(errno)); } @@ -901,27 +979,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size } #if defined(LINUX) - // On Linux, different containerized processes that share the same /tmp - // directory (e.g., with "docker --volume ...") may have the same pid and - // try to use the same file. To avoid conflicts among such - // processes, we allow only one of them (the winner of the flock() call) - // to write to the file. All the other processes will give up and will - // have perfdata disabled. - // - // Note that the flock will be automatically given up when the winner - // process exits. - // - // The locking protocol works only with other JVMs that have the JDK-8286030 - // fix. If you are sharing the /tmp difrectory among different containers, - // do not use older JVMs that don't have this fix, or the behavior is undefined. - int n; - RESTARTABLE(::flock(fd, LOCK_EX|LOCK_NB), n); - if (n != 0) { - log_warning(perf, memops)("Cannot use file %s/%s because %s (errno = %d)", dirname, filename, - (errno == EWOULDBLOCK) ? - "it is locked by another process" : - "flock() failed", errno); - ::close(fd); + if (fd == OS_ERR) { return -1; } #endif diff --git a/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S b/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S new file mode 100644 index 00000000000..b82053d37b9 --- /dev/null +++ b/src/hotspot/os_cpu/linux_aarch64/ic_ivau_probe_linux_aarch64.S @@ -0,0 +1,69 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "defs.S.inc" + + # Probe whether IC IVAU is trapped. + # + # Returns 1 if IC IVAU is trapped (did not fault), 0 if not trapped + # (faulted on VA 0x0, signal handler redirected to continuation). + # + # int ic_ivau_probe(void); +DECLARE_FUNC(ic_ivau_probe): +DECLARE_FUNC(_ic_ivau_probe_fault): + ic ivau, xzr + mov x0, #1 + ret +DECLARE_FUNC(_ic_ivau_probe_continuation): + mov x0, #0 + ret + +/* Emit .note.gnu.property section in case of PAC or BTI being enabled. */ +#ifdef __ARM_FEATURE_BTI_DEFAULT + #ifdef __ARM_FEATURE_PAC_DEFAULT + #define GNU_PROPERTY_AARCH64_FEATURE 3 + #else + #define GNU_PROPERTY_AARCH64_FEATURE 1 + #endif +#else + #ifdef __ARM_FEATURE_PAC_DEFAULT + #define GNU_PROPERTY_AARCH64_FEATURE 2 + #else + #define GNU_PROPERTY_AARCH64_FEATURE 0 + #endif +#endif + +#if (GNU_PROPERTY_AARCH64_FEATURE != 0) + .pushsection .note.gnu.property, "a" + .align 3 + .long 4 /* name length */ + .long 0x10 /* data length */ + .long 5 /* note type: NT_GNU_PROPERTY_TYPE_0 */ + .string "GNU" /* vendor name */ + .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4 /* pr_datasze */ + .long GNU_PROPERTY_AARCH64_FEATURE + .long 0 + .popsection +#endif diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp new file mode 100644 index 00000000000..11911a48e06 --- /dev/null +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "runtime/icache.hpp" +#include "utilities/globalDefinitions.hpp" + +DEBUG_ONLY(THREAD_LOCAL AArch64ICacheInvalidationContext* AArch64ICacheInvalidationContext::_current_context = nullptr;) diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index 8fbaa7a6b6e..5121a875701 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -26,6 +26,11 @@ #ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP #define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP +#include "memory/allocation.hpp" +#include "runtime/vm_version.hpp" +#include "utilities/globalDefinitions.hpp" +#include "vm_version_aarch64.hpp" + // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially // has to be flushed. @@ -37,8 +42,105 @@ class ICache : public AbstractICache { __builtin___clear_cache((char *)addr, (char *)(addr + 4)); } static void invalidate_range(address start, int nbytes) { - __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + if (NeoverseN1ICacheErratumMitigation) { + assert(VM_Version::is_cache_idc_enabled(), + "Expect CTR_EL0.IDC to be enabled for Neoverse N1 with erratum " + "1542419"); + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + assert(VM_Version::is_ic_ivau_trapped(), "Expect 'ic ivau, xzr' to be trapped"); + asm volatile("dsb ish \n" + "ic ivau, xzr \n" + "dsb ish \n" + "isb \n" + : : : "memory"); + } else { + __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + } } }; +class AArch64ICacheInvalidationContext : StackObj { + private: + +#ifdef ASSERT + static THREAD_LOCAL AArch64ICacheInvalidationContext* _current_context; +#endif + + bool _has_modified_code; + + public: + NONCOPYABLE(AArch64ICacheInvalidationContext); + + AArch64ICacheInvalidationContext() + : _has_modified_code(false) { + assert(_current_context == nullptr, "nested ICacheInvalidationContext not supported"); +#ifdef ASSERT + _current_context = this; +#endif + } + + ~AArch64ICacheInvalidationContext() { + DEBUG_ONLY(_current_context = nullptr); + + if (!_has_modified_code || !UseSingleICacheInvalidation) { + return; + } + + assert(VM_Version::is_cache_idc_enabled(), "Expect CTR_EL0.IDC to be enabled"); + + asm volatile("dsb ish" : : : "memory"); + + if (NeoverseN1ICacheErratumMitigation) { + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + assert(VM_Version::is_ic_ivau_trapped(), "Expect 'ic ivau, xzr' to be trapped"); + + // Errata 1542419: Neoverse N1 cores with the 'COHERENT_ICACHE' feature + // may fetch stale instructions when software depends on + // prefetch-speculation-protection instead of explicit synchronization. + // + // Neoverse-N1 implementation mitigates the errata 1542419 with a + // workaround: + // - Disable coherent icache. + // - Trap IC IVAU instructions. + // - Execute: + // - tlbi vae3is, xzr + // - dsb sy + // - Ignore trapped IC IVAU instructions. + // + // `tlbi vae3is, xzr` invalidates all translation entries (all VAs, all + // possible levels). It waits for all memory accesses using in-scope old + // translation information to complete before it is considered complete. + // + // As this workaround has significant overhead, Arm Neoverse N1 (MP050) + // Software Developer Errata Notice version 29.0 suggests: + // + // "Since one TLB inner-shareable invalidation is enough to avoid this + // erratum, the number of injected TLB invalidations should be minimized + // in the trap handler to mitigate the performance impact due to this + // workaround." + // As the address for icache invalidation is not relevant and + // IC IVAU instruction is ignored, we use XZR in it. + asm volatile( + "ic ivau, xzr \n" + "dsb ish \n" + : + : + : "memory"); + } else { + assert(VM_Version::is_cache_dic_enabled(), "Expect CTR_EL0.DIC to be enabled"); + } + asm volatile("isb" : : : "memory"); + } + + void set_has_modified_code() { + _has_modified_code = true; + } +}; + +#define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext + #endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index da9e7e159f1..67e0569bf31 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -77,6 +77,11 @@ #define REG_LR 30 #define REG_BCP 22 +// IC IVAU trap probe. +// Defined in ic_ivau_probe_linux_aarch64.S. +extern "C" char _ic_ivau_probe_fault[] __attribute__ ((visibility ("hidden"))); +extern "C" char _ic_ivau_probe_continuation[] __attribute__ ((visibility ("hidden"))); + NOINLINE address os::current_stack_pointer() { return (address)__builtin_frame_address(0); } @@ -228,6 +233,12 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, } } + // IC IVAU trap probe during VM_Version initialization. + // If IC IVAU is not trapped, it faults on unmapped VA 0x0. + if (sig == SIGSEGV && pc == (address)_ic_ivau_probe_fault) { + stub = (address)_ic_ivau_probe_continuation; + } + if (thread->thread_state() == _thread_in_Java) { // Java thread running in Java code => find exception handler if any // a fault inside compiled code, the interpreter, or a stub diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index 168fc622a0b..ee2d3013c4c 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -31,6 +31,10 @@ #include #include +// IC IVAU trap probe. +// Defined in ic_ivau_probe_linux_aarch64.S. +extern "C" int ic_ivau_probe(void); + #ifndef HWCAP_AES #define HWCAP_AES (1<<3) #endif @@ -182,6 +186,12 @@ void VM_Version::get_os_cpu_info() { _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4; _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4; + _cache_idc_enabled = ((ctr_el0 >> 28) & 0x1) != 0; + _cache_dic_enabled = ((ctr_el0 >> 29) & 0x1) != 0; + + // Probe whether IC IVAU is trapped. + // Must run before VM_Version::initialize() sets NeoverseN1ICacheErratumMitigation. + _ic_ivau_trapped = (ic_ivau_probe() == 1); if (!(dczid_el0 & 0x10)) { _zva_length = 4 << (dczid_el0 & 0xf); diff --git a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp index a360ee342be..a2c8f0c685c 100644 --- a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp @@ -27,10 +27,24 @@ // Included in runtime/prefetch.inline.hpp +#include + +// __prefetch2(addr, prfop) emits a PRFM instruction. +// The prfop encoding is: +// type: PLD = 00, PLI = 01, PST = 10 +// target: L1 = 00, L2 = 01, L3 = 10 +// policy: KEEP = 0, STRM = 1 + inline void Prefetch::read (const void *loc, intx interval) { + if (interval >= 0) { + __prefetch2((const char*) loc + interval, /* PLD + L1 + KEEP */ 0); + } } inline void Prefetch::write(void *loc, intx interval) { + if (interval >= 0) { + __prefetch2((char*) loc + interval, /* PST + L1 + KEEP */ 16); + } } #endif // OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP diff --git a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp index 645fbe99a22..575eabc97dd 100644 --- a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp +++ b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp @@ -27,7 +27,18 @@ // Included in runtime/prefetch.inline.hpp -inline void Prefetch::read (const void *loc, intx interval) {} -inline void Prefetch::write(void *loc, intx interval) {} +#include + +inline void Prefetch::read (const void *loc, intx interval) { + if (interval >= 0) { + _mm_prefetch((const char*) loc + interval, _MM_HINT_T0); + } +} + +inline void Prefetch::write(void *loc, intx interval) { + if (interval >= 0) { + _mm_prefetch((const char*) loc + interval, _MM_HINT_T0); + } +} #endif // OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index c6078c0ceee..6a288e0dad0 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -1136,7 +1136,7 @@ void AsmRemarks::clear() { uint AsmRemarks::print(uint offset, outputStream* strm) const { uint count = 0; const char* prefix = " ;; "; - const char* remstr = _remarks->lookup(offset); + const char* remstr = (_remarks ? _remarks->lookup(offset) : nullptr); while (remstr != nullptr) { strm->bol(); strm->print("%s", prefix); diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 63764dd113a..38f563935e0 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -278,11 +278,9 @@ bool Runtime1::initialize(BufferBlob* blob) { if (!generate_blob_for(blob, id)) { return false; } - if (id == StubId::c1_forward_exception_id) { - // publish early c1 stubs at this point so later stubs can refer to them - AOTCodeCache::init_early_c1_table(); - } } + // disallow any further c1 stub generation + AOTCodeCache::set_c1_stubs_complete(); // printing #ifndef PRODUCT if (PrintSimpleStubs) { diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 404693f5cee..a9ea6fbea11 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -34,6 +34,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" @@ -86,9 +87,6 @@ #include "utilities/macros.hpp" #include "utilities/ostream.hpp" #include "utilities/utf8.hpp" -#if INCLUDE_CDS -#include "classfile/systemDictionaryShared.hpp" -#endif // We generally try to create the oops directly when parsing, rather than // allocating temporary data structures and copying the bytes twice. A @@ -5256,6 +5254,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, if (!is_internal()) { ik->print_class_load_logging(_loader_data, module_entry, _stream); + if (CDSConfig::is_dumping_archive()) { + SystemDictionaryShared::check_code_source(ik, _stream); + } if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION && ik->major_version() == JVM_CLASSFILE_MAJOR_VERSION && diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 58d432a628c..fd30fc6766f 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -202,6 +202,20 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) { return info; } +void SystemDictionaryShared::check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) { + if (CDSConfig::is_dumping_preimage_static_archive() && !is_builtin_loader(ik->class_loader_data())) { + if (cfs == nullptr || cfs->source() == nullptr || strncmp(cfs->source(), "file:", 5) != 0) { + // AOT cache filtering: + // For non-built-in loaders, cache only the classes that have a file: code source, so + // we can avoid caching dynamically generated classes that are likely to change from + // run to run. This is similar to the filtering in ClassListWriter::write_to_stream() + // for the classic CDS static archive. + SystemDictionaryShared::log_exclusion(ik, "Not loaded from \"file:\" code source"); + SystemDictionaryShared::set_excluded(ik); + } + } +} + bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) { assert_lock_strong(DumpTimeTable_lock); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 33b245e26fc..c837a386344 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -235,6 +235,7 @@ class SystemDictionaryShared: public SystemDictionary { static void update_shared_entry(InstanceKlass* klass, int id); static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs); + static void check_code_source(InstanceKlass* ik, const ClassFileStream* cfs) NOT_CDS_RETURN; static InstanceKlass* lookup_from_stream(Symbol* class_name, Handle class_loader, Handle protection_domain, diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 938cf4eaa41..d3888d1b7eb 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -33,13 +33,18 @@ #include "classfile/javaAssertions.hpp" #include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" +#include "gc/shared/barrierSetAssembler.hpp" +#include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcConfig.hpp" #include "logging/logStream.hpp" #include "memory/memoryReserver.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "prims/upcallLinker.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/globals_extension.hpp" +#include "runtime/icache.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.inline.hpp" @@ -73,11 +78,23 @@ const char* aot_code_entry_kind_name[] = { #undef DECL_KIND_STRING }; +// Stream to printing AOTCodeCache loading failure. +// Print to error channel when -XX:AOTMode is set to "on" +static LogStream& load_failure_log() { + static LogStream err_stream(LogLevel::Error, LogTagSetMapping::tagset()); + static LogStream dbg_stream(LogLevel::Debug, LogTagSetMapping::tagset()); + if (RequireSharedSpaces) { + return err_stream; + } else { + return dbg_stream; + } +} + static void report_load_failure() { if (AbortVMOnAOTCodeFailure) { vm_exit_during_initialization("Unable to use AOT Code Cache.", nullptr); } - log_info(aot, codecache, init)("Unable to use AOT Code Cache."); + load_failure_log().print_cr("Unable to use AOT Code Cache."); AOTCodeCache::disable_caching(); } @@ -86,7 +103,7 @@ static void report_store_failure() { tty->print_cr("Unable to create AOT Code Cache."); vm_abort(false); } - log_info(aot, codecache, exit)("Unable to create AOT Code Cache."); + log_error(aot, codecache, exit)("Unable to create AOT Code Cache."); AOTCodeCache::disable_caching(); } @@ -156,10 +173,13 @@ static uint32_t encode_id(AOTCodeEntry::Kind kind, int id) { } else if (kind == AOTCodeEntry::C1Blob) { assert(StubInfo::is_c1(static_cast(id)), "not a c1 blob id %d", id); return id; - } else { - // kind must be AOTCodeEntry::C2Blob + } else if (kind == AOTCodeEntry::C2Blob) { assert(StubInfo::is_c2(static_cast(id)), "not a c2 blob id %d", id); return id; + } else { + // kind must be AOTCodeEntry::StubGenBlob + assert(StubInfo::is_stubgen(static_cast(id)), "not a stubgen blob id %d", id); + return id; } } @@ -184,9 +204,6 @@ void AOTCodeCache::initialize() { return; // AOTCache must be specified to dump and use AOT code } - // Disable stubs caching until JDK-8357398 is fixed. - FLAG_SET_ERGO(AOTStubCaching, false); - if (VerifyOops) { // Disable AOT stubs caching when VerifyOops flag is on. // Verify oops code generated a lot of C strings which overflow @@ -284,6 +301,19 @@ bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) { return true; } +// Called after continuations_init() when continuation stub callouts +// have been initialized +void AOTCodeCache::init3() { + if (opened_cache == nullptr) { + return; + } + // initialize external routines for continuations so we can save + // generated continuation blob that references them + AOTCodeAddressTable* table = opened_cache->_table; + assert(table != nullptr, "should be initialized already"); + table->init_extrs2(); +} + void AOTCodeCache::dump() { if (is_on()) { assert(is_on_for_dump(), "should be called only when dumping AOT code"); @@ -342,6 +372,7 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : log_info (aot, codecache, init)("Loaded %u AOT code entries from AOT Code Cache", _load_header->entries_count()); log_debug(aot, codecache, init)(" Adapters: total=%u", _load_header->adapters_count()); log_debug(aot, codecache, init)(" Shared Blobs: total=%u", _load_header->shared_blobs_count()); + log_debug(aot, codecache, init)(" StubGen Blobs: total=%d", _load_header->stubgen_blobs_count()); log_debug(aot, codecache, init)(" C1 Blobs: total=%u", _load_header->C1_blobs_count()); log_debug(aot, codecache, init)(" C2 Blobs: total=%u", _load_header->C2_blobs_count()); log_debug(aot, codecache, init)(" AOT code cache size: %u bytes", _load_header->cache_size()); @@ -359,131 +390,83 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : _table = new AOTCodeAddressTable(); } -void AOTCodeCache::init_early_stubs_table() { +void AOTCodeCache::add_stub_entries(StubId stub_id, address start, GrowableArray
*entries, int begin_idx) { + EntryId entry_id = StubInfo::entry_base(stub_id); + add_stub_entry(entry_id, start); + // skip past first entry + entry_id = StubInfo::next_in_stub(stub_id, entry_id); + // now check for any more entries + int count = StubInfo::entry_count(stub_id) - 1; + assert(start != nullptr, "invalid start address for stub %s", StubInfo::name(stub_id)); + assert(entries == nullptr || begin_idx + count <= entries->length(), "sanity"); + // write any extra entries + for (int i = 0; i < count; i++) { + assert(entry_id != EntryId::NO_ENTRYID, "not enough entries for stub %s", StubInfo::name(stub_id)); + address a = entries->at(begin_idx + i); + add_stub_entry(entry_id, a); + entry_id = StubInfo::next_in_stub(stub_id, entry_id); + } + assert(entry_id == EntryId::NO_ENTRYID, "too many entries for stub %s", StubInfo::name(stub_id)); +} + +void AOTCodeCache::add_stub_entry(EntryId entry_id, address a) { + if (a != nullptr) { + if (_table != nullptr) { + log_trace(aot, codecache, stubs)("Publishing stub entry %s at address " INTPTR_FORMAT, StubInfo::name(entry_id), p2i(a)); + return _table->add_stub_entry(entry_id, a); + } + } +} + +void AOTCodeCache::set_shared_stubs_complete() { AOTCodeAddressTable* table = addr_table(); if (table != nullptr) { - table->init_early_stubs(); + table->set_shared_stubs_complete(); } } -void AOTCodeCache::init_shared_blobs_table() { +void AOTCodeCache::set_c1_stubs_complete() { AOTCodeAddressTable* table = addr_table(); if (table != nullptr) { - table->init_shared_blobs(); + table->set_c1_stubs_complete(); } } -void AOTCodeCache::init_early_c1_table() { +void AOTCodeCache::set_c2_stubs_complete() { AOTCodeAddressTable* table = addr_table(); if (table != nullptr) { - table->init_early_c1(); + table->set_c2_stubs_complete(); } } -// macro to record which flags are set -- flag_type selects the -// relevant accessor e.g. set_flag, set_x86_flag, set_x86_use_flag. -// n.b. flag_enum_name and global_flag_name are both needed because we -// don't have consistent conventions for naming global flags e.g. -// EnableContended vs UseMulAddIntrinsic vs UseCRC32Intrinsics - -#define RECORD_FLAG(flag_type, flag_enum_name, global_flag_name) \ - if (global_flag_name) { \ - set_ ## flag_type ## flag(flag_enum_name); \ +void AOTCodeCache::set_stubgen_stubs_complete() { + AOTCodeAddressTable* table = addr_table(); + if (table != nullptr) { + table->set_stubgen_stubs_complete(); } +} void AOTCodeCache::Config::record(uint cpu_features_offset) { - _flags = 0; -#ifdef ASSERT - set_flag(debugVM); -#endif - RECORD_FLAG(, compressedOops, UseCompressedOops); - RECORD_FLAG(, useTLAB, UseTLAB); - if (JavaAssertions::systemClassDefault()) { - set_flag(systemClassAssertions); - } - if (JavaAssertions::userClassDefault()) { - set_flag(userClassAssertions); - } - RECORD_FLAG(, enableContendedPadding, EnableContended); - RECORD_FLAG(, restrictContendedPadding, RestrictContended); - _compressedOopShift = CompressedOops::shift(); +#define AOTCODECACHE_SAVE_VAR(type, name) _saved_ ## name = name; +#define AOTCODECACHE_SAVE_FUN(type, name, fun) _saved_ ## name = fun; + + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_SAVE_VAR, AOTCODECACHE_SAVE_FUN); + + // Special configs that cannot be checked with macros _compressedOopBase = CompressedOops::base(); - _compressedKlassShift = CompressedKlassPointers::shift(); - _contendedPaddingWidth = ContendedPaddingWidth; - _gc = (uint)Universe::heap()->kind(); - _optoLoopAlignment = (uint)OptoLoopAlignment; - _codeEntryAlignment = (uint)CodeEntryAlignment; - _allocatePrefetchLines = (uint)AllocatePrefetchLines; - _allocateInstancePrefetchLines = (uint)AllocateInstancePrefetchLines; - _allocatePrefetchDistance = (uint)AllocatePrefetchDistance; - _allocatePrefetchStepSize = (uint)AllocatePrefetchStepSize; - _use_intrinsics_flags = 0; - RECORD_FLAG(use_, useCRC32, UseCRC32Intrinsics); - RECORD_FLAG(use_, useCRC32C, UseCRC32CIntrinsics); -#ifdef COMPILER2 - _maxVectorSize = (uint)MaxVectorSize; - _arrayOperationPartialInlineSize = (uint)ArrayOperationPartialInlineSize; - RECORD_FLAG(use_, useMultiplyToLen, UseMultiplyToLenIntrinsic); - RECORD_FLAG(use_, useSquareToLen, UseSquareToLenIntrinsic); - RECORD_FLAG(use_, useMulAdd, UseMulAddIntrinsic); - RECORD_FLAG(use_, useMontgomeryMultiply, UseMontgomeryMultiplyIntrinsic); - RECORD_FLAG(use_, useMontgomerySquare, UseMontgomerySquareIntrinsic); -#endif // COMPILER2 - RECORD_FLAG(use_, useChaCha20, UseChaCha20Intrinsics); - RECORD_FLAG(use_, useDilithium, UseDilithiumIntrinsics); - RECORD_FLAG(use_, useKyber, UseKyberIntrinsics); - RECORD_FLAG(use_, useBASE64, UseBASE64Intrinsics); - RECORD_FLAG(use_, useAdler32, UseAdler32Intrinsics); - RECORD_FLAG(use_, useAES, UseAESIntrinsics); - RECORD_FLAG(use_, useAESCTR, UseAESCTRIntrinsics); - RECORD_FLAG(use_, useGHASH, UseGHASHIntrinsics); - RECORD_FLAG(use_, useMD5, UseMD5Intrinsics); - RECORD_FLAG(use_, useSHA1, UseSHA1Intrinsics); - RECORD_FLAG(use_, useSHA256, UseSHA256Intrinsics); - RECORD_FLAG(use_, useSHA512, UseSHA512Intrinsics); - RECORD_FLAG(use_, useSHA3, UseSHA3Intrinsics); - RECORD_FLAG(use_, usePoly1305, UsePoly1305Intrinsics); - RECORD_FLAG(use_, useVectorizedMismatch,UseVectorizedMismatchIntrinsic ); - RECORD_FLAG(use_, useSecondarySupersTable, UseSecondarySupersTable); + #if defined(X86) && !defined(ZERO) - _avx3threshold = (uint)AVX3Threshold; - _useAVX = (uint)UseAVX; - _x86_flags = 0; - RECORD_FLAG(x86_, x86_enableX86ECoreOpts, EnableX86ECoreOpts); - RECORD_FLAG(x86_, x86_useUnalignedLoadStores, UseUnalignedLoadStores); - RECORD_FLAG(x86_, x86_useAPX, UseAPX); - - _x86_use_intrinsics_flags = 0; - RECORD_FLAG(x86_use_, x86_useLibm, UseLibmIntrinsic); - RECORD_FLAG(x86_use_, x86_useIntPoly, UseIntPolyIntrinsics); -#endif // defined(X86) && !defined(ZERO) + _useUnalignedLoadStores = UseUnalignedLoadStores; +#endif + #if defined(AARCH64) && !defined(ZERO) - _prefetchCopyIntervalInBytes = (uint)PrefetchCopyIntervalInBytes; - _blockZeroingLowLimit = (uint)BlockZeroingLowLimit; - _softwarePrefetchHintDistance = (uint)SoftwarePrefetchHintDistance; - _useSVE = (uint)UseSVE; - _aarch64_flags = 0; - RECORD_FLAG(aarch64_, aarch64_avoidUnalignedAccesses, AvoidUnalignedAccesses); - RECORD_FLAG(aarch64_, aarch64_useSIMDForMemoryOps, UseSIMDForMemoryOps); - RECORD_FLAG(aarch64_, aarch64_useSIMDForArrayEquals, UseSIMDForArrayEquals); - RECORD_FLAG(aarch64_, aarch64_useSIMDForSHA3, UseSIMDForSHA3Intrinsic); - RECORD_FLAG(aarch64_, aarch64_useLSE, UseLSE); - - _aarch64_use_intrinsics_flags = 0; - RECORD_FLAG(aarch64_use_, aarch64_useBlockZeroing, UseBlockZeroing); - RECORD_FLAG(aarch64_use_, aarch64_useSIMDForBigIntegerShift, UseSIMDForBigIntegerShiftIntrinsics); - RECORD_FLAG(aarch64_use_, aarch64_useSimpleArrayEquals, UseSimpleArrayEquals); - RECORD_FLAG(aarch64_use_, aarch64_useSecondarySupersCache, UseSecondarySupersCache); -#endif // defined(AARCH64) && !defined(ZERO) -#if INCLUDE_JVMCI - _enableJVMCI = (uint)EnableJVMCI; + _avoidUnalignedAccesses = AvoidUnalignedAccesses; #endif + _cpu_features_offset = cpu_features_offset; } -#undef RECORD_FLAG - bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const { LogStreamHandle(Debug, aot, codecache, init) log; uint offset = _cpu_features_offset; @@ -511,271 +494,114 @@ bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const { } } } else { - if (log.is_enabled()) { + if (load_failure_log().is_enabled()) { ResourceMark rm; // required for stringStream::as_string() stringStream ss; char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size()); VM_Version::store_cpu_features(runtime_cpu_features); VM_Version::get_missing_features_name(cached_cpu_features_buffer, runtime_cpu_features, ss); - log.print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string()); + load_failure_log().print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string()); } return false; } return true; } -// macro to do *standard* flag eq checks -- flag_type selects the -// relevant accessor e.g. test_flag, test_x86_flag, test_x86_use_flag. -// n.b. flag_enum_name and global_flag_name are both needed because we -// don't have consistent conventions for naming global flags e.g. -// EnableContended vs UseMulAddIntrinsic vs UseCRC32Intrinsics +#define AOTCODECACHE_DISABLED_MSG "AOT Code Cache disabled: it was created with %s = " -#define CHECK_FLAG(flag_type, flag_enum_name, global_flag_name) \ - if (test_ ## flag_type ## flag(flag_enum_name) != global_flag_name) { \ - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with " # global_flag_name " = %s vs current %s" , (global_flag_name ? "false" : "true"), (global_flag_name ? "true" : "false")); \ - return false; \ - } +// Special case, print "GC = ..." to be more understandable. +inline void log_config_mismatch(CollectedHeap::Name saved, CollectedHeap::Name current, const char* name/*unused*/) { + load_failure_log().print_cr("AOT Code Cache disabled: it was created with GC = \"%s\" vs current \"%s\"", + GCConfig::hs_err_name(saved), GCConfig::hs_err_name(current)); +} -bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { - // First checks affect all cached AOT code -#ifdef ASSERT - if (!test_flag(debugVM)) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by product VM, it can't be used by debug VM"); - return false; - } -#else - if (test_flag(debugVM)) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by debug VM, it can't be used by product VM"); - return false; - } -#endif +inline void log_config_mismatch(bool saved, bool current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%s vs current %s", name, + saved ? "true" : "false", current ? "true" : "false"); +} - CollectedHeap::Name aot_gc = (CollectedHeap::Name)_gc; - if (aot_gc != Universe::heap()->kind()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with different GC: %s vs current %s", GCConfig::hs_err_name(aot_gc), GCConfig::hs_err_name()); - return false; - } +inline void log_config_mismatch(int saved, int current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%d vs current %d", name, saved, current); +} + +inline void log_config_mismatch(uint saved, uint current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%u vs current %u", name, saved, current); +} + +#ifdef _LP64 +inline void log_config_mismatch(intx saved, intx current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%zd vs current %zd", name, saved, current); +} + +inline void log_config_mismatch(uintx saved, uintx current, const char* name) { + load_failure_log().print_cr(AOTCODECACHE_DISABLED_MSG "%zu vs current %zu", name, saved, current); +} +#endif - if (_compressedKlassShift != (uint)CompressedKlassPointers::shift()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CompressedKlassPointers::shift() = %d vs current %d", _compressedKlassShift, CompressedKlassPointers::shift()); +template +bool check_config(T saved, T current, const char* name) { + if (saved != current) { + log_config_mismatch(saved, current, name); return false; + } else { + return true; } +} +bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { // check CPU features before checking flags that may be // auto-configured in response to them if (!verify_cpu_features(cache)) { return false; } - // change to EnableContended can affect validity of nmethods - CHECK_FLAG(, enableContendedPadding, EnableContended); - // change to RestrictContended can affect validity of nmethods - CHECK_FLAG(, restrictContendedPadding, RestrictContended); - // Tests for config options which might affect validity of adapters, // stubs or nmethods. Currently we take a pessemistic stand and // drop the whole cache if any of these are changed. - // change to opto alignment can affect performance of array copy - // stubs and nmethods - if (_optoLoopAlignment != (uint)OptoLoopAlignment) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with OptoLoopAlignment = %d vs current %d", (int)_optoLoopAlignment, (int)OptoLoopAlignment); - return false; - } +#define AOTCODECACHE_CHECK_VAR(type, name) \ + if (!check_config(_saved_ ## name, name, #name)) { return false; } +#define AOTCODECACHE_CHECK_FUN(type, name, fun) \ + if (!check_config(_saved_ ## name, fun, #fun)) { return false; } - // change to CodeEntryAlignment can affect performance of array - // copy stubs and nmethods - if (_codeEntryAlignment != CodeEntryAlignment) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CodeEntryAlignment = %d vs current %d", _codeEntryAlignment, CodeEntryAlignment); - return false; - } + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_CHECK_VAR, AOTCODECACHE_CHECK_FUN); - // changing Prefetch configuration can affect validity of nmethods - // and stubs - if (_allocatePrefetchLines != (uint)AllocatePrefetchLines) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchLines, (int)AllocatePrefetchLines); - return false; - } - if (_allocateInstancePrefetchLines != (uint)AllocateInstancePrefetchLines) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocateInstancePrefetchLines, (int)AllocateInstancePrefetchLines); - return false; - } - if (_allocatePrefetchDistance != (uint)AllocatePrefetchDistance) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchDistance, (int)AllocatePrefetchDistance); - return false; - } - if (_allocatePrefetchStepSize != (uint)AllocatePrefetchStepSize) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with = %d vs current %d", (int)_allocatePrefetchStepSize, (int)AllocatePrefetchStepSize); - return false; - } - - // check intrinsic use settings are compatible + // Special configs that cannot be checked with macros - CHECK_FLAG(use_, useCRC32, UseCRC32Intrinsics); - CHECK_FLAG(use_, useCRC32C, UseCRC32CIntrinsics); - -#ifdef COMPILER2 - // change to MaxVectorSize can affect validity of array copy/fill - // stubs - if (_maxVectorSize != (uint)MaxVectorSize) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with MaxVectorSize = %d vs current %d", (int)_maxVectorSize, (int)MaxVectorSize); - return false; - } - - // changing ArrayOperationPartialInlineSize can affect validity of - // nmethods and stubs - if (_arrayOperationPartialInlineSize != (uint)ArrayOperationPartialInlineSize) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with ArrayOperationPartialInlineSize = %d vs current %d", (int)_arrayOperationPartialInlineSize, (int)ArrayOperationPartialInlineSize); + if ((_compressedOopBase == nullptr || CompressedOops::base() == nullptr) && (_compressedOopBase != CompressedOops::base())) { + load_failure_log().print_cr("AOT Code Cache disabled: incompatible CompressedOops::base(): %p vs current %p", + _compressedOopBase, CompressedOops::base()); return false; } - CHECK_FLAG(use_, useMultiplyToLen, UseMultiplyToLenIntrinsic); - CHECK_FLAG(use_, useSquareToLen, UseSquareToLenIntrinsic); - CHECK_FLAG(use_, useMulAdd, UseMulAddIntrinsic); - CHECK_FLAG(use_, useMontgomeryMultiply,UseMontgomeryMultiplyIntrinsic); - CHECK_FLAG(use_, useMontgomerySquare, UseMontgomerySquareIntrinsic); -#endif // COMPILER2 - CHECK_FLAG(use_, useChaCha20, UseChaCha20Intrinsics); - CHECK_FLAG(use_, useDilithium, UseDilithiumIntrinsics); - CHECK_FLAG(use_, useKyber, UseKyberIntrinsics); - CHECK_FLAG(use_, useBASE64, UseBASE64Intrinsics); - CHECK_FLAG(use_, useAES, UseAESIntrinsics); - CHECK_FLAG(use_, useAESCTR, UseAESCTRIntrinsics); - CHECK_FLAG(use_, useGHASH, UseGHASHIntrinsics); - CHECK_FLAG(use_, useMD5, UseMD5Intrinsics); - CHECK_FLAG(use_, useSHA1, UseSHA1Intrinsics); - CHECK_FLAG(use_, useSHA256, UseSHA256Intrinsics); - CHECK_FLAG(use_, useSHA512, UseSHA512Intrinsics); - CHECK_FLAG(use_, useSHA3, UseSHA3Intrinsics); - CHECK_FLAG(use_, usePoly1305, UsePoly1305Intrinsics); - CHECK_FLAG(use_, useVectorizedMismatch, UseVectorizedMismatchIntrinsic); - CHECK_FLAG(use_, useSecondarySupersTable, UseSecondarySupersTable); #if defined(X86) && !defined(ZERO) - // change to AVX3Threshold may affect validity of array copy stubs - // and nmethods - if (_avx3threshold != (uint)AVX3Threshold) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with AVX3Threshold = %d vs current %d", (int)_avx3threshold, AVX3Threshold); - return false; - } - - // change to UseAVX may affect validity of array copy stubs and - // nmethods - if (_useAVX != (uint)UseAVX) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with useAVX = %d vs current %d", (int)_useAVX, UseAVX); - return false; - } - - // change to EnableX86ECoreOpts may affect validity of nmethods - CHECK_FLAG(x86_, x86_enableX86ECoreOpts, EnableX86ECoreOpts); - // switching off UseUnalignedLoadStores can affect validity of fill // stubs - if (test_x86_flag(x86_useUnalignedLoadStores) && !UseUnalignedLoadStores) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseUnalignedLoadStores = true vs current = false"); + if (_useUnalignedLoadStores && !UseUnalignedLoadStores) { + log_config_mismatch(_useUnalignedLoadStores, UseUnalignedLoadStores, "UseUnalignedLoadStores"); return false; } - - // change to UseAPX can affect validity of nmethods and stubs - CHECK_FLAG(x86_, x86_useAPX, UseAPX); - - // check x86-specific intrinsic use settings are compatible - - CHECK_FLAG(x86_use_, x86_useLibm, UseLibmIntrinsic); - CHECK_FLAG(x86_use_, x86_useIntPoly, UseIntPolyIntrinsics); #endif // defined(X86) && !defined(ZERO) #if defined(AARCH64) && !defined(ZERO) - // change to PrefetchCopyIntervalInBytes may affect validity of - // array copy stubs - if (_prefetchCopyIntervalInBytes != (uint)PrefetchCopyIntervalInBytes) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with PrefetchCopyIntervalInBytes = %d vs current %d", (int)_prefetchCopyIntervalInBytes, (int)PrefetchCopyIntervalInBytes); - return false; - } - - // change to BlockZeroingLowLimit may affect validity of array fill - // stubs - if (_blockZeroingLowLimit != (uint)BlockZeroingLowLimit) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with BlockZeroingLowLimit = %d vs current %d", (int)_blockZeroingLowLimit, (int)BlockZeroingLowLimit); - return false; - } - - // change to SoftwarePrefetchHintDistance may affect validity of array fill - // stubs - if (_softwarePrefetchHintDistance != (uint)SoftwarePrefetchHintDistance) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with SoftwarePrefetchHintDistance = %d vs current %d", (int)_softwarePrefetchHintDistance, (int)SoftwarePrefetchHintDistance); - return false; - } - - // change to UseSVE may affect validity of stubs and nmethods - if (_useSVE != (uint)UseSVE) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseSVE = %d vs current %d",(int)_useSVE, UseSVE); - return false; - } - // switching on AvoidUnalignedAccesses may affect validity of array // copy stubs and nmethods - if (!test_aarch64_flag(aarch64_avoidUnalignedAccesses) && AvoidUnalignedAccesses) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with AvoidUnalignedAccesses = false vs current = true"); + if (!_avoidUnalignedAccesses && AvoidUnalignedAccesses) { + log_config_mismatch(_avoidUnalignedAccesses, AvoidUnalignedAccesses, "AvoidUnalignedAccesses"); return false; } - - // change to UseSIMDForMemoryOps may affect validity of array - // copy stubs and nmethods - CHECK_FLAG(aarch64_, aarch64_useSIMDForMemoryOps, UseSIMDForMemoryOps); - // change to UseSIMDForArrayEquals may affect validity of array - // copy stubs and nmethods - CHECK_FLAG(aarch64_, aarch64_useSIMDForArrayEquals, UseSIMDForArrayEquals); - // change to useSIMDForSHA3 may affect validity of SHA3 stubs - CHECK_FLAG(aarch64_, aarch64_useSIMDForSHA3, UseSIMDForSHA3Intrinsic); - // change to UseLSE may affect validity of stubs and nmethods - CHECK_FLAG(aarch64_, aarch64_useLSE, UseLSE); - - // check aarch64-specific intrinsic use settings are compatible - - CHECK_FLAG(aarch64_use_, aarch64_useBlockZeroing, UseBlockZeroing); - CHECK_FLAG(aarch64_use_, aarch64_useSIMDForBigIntegerShift, UseSIMDForBigIntegerShiftIntrinsics); - CHECK_FLAG(aarch64_use_, aarch64_useSimpleArrayEquals, UseSimpleArrayEquals); - CHECK_FLAG(aarch64_use_, aarch64_useSecondarySupersCache, UseSecondarySupersCache); #endif // defined(AARCH64) && !defined(ZERO) -#if INCLUDE_JVMCI - // change to EnableJVMCI will affect validity of adapters and - // nmethods - if (_enableJVMCI != (uint)EnableJVMCI) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with EnableJVMCI = %s vs current %s", (_enableJVMCI ? "true" : "false"), (EnableJVMCI ? "true" : "false")); - return false; - } -#endif // INCLUDE_JVMCI - - // The following checks do not affect AOT adapters caching - - if (test_flag(compressedOops) != UseCompressedOops) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedOops = %s", UseCompressedOops ? "false" : "true"); - AOTStubCaching = false; - } - if (_compressedOopShift != (uint)CompressedOops::shift()) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with different CompressedOops::shift(): %d vs current %d", _compressedOopShift, CompressedOops::shift()); - AOTStubCaching = false; - } - - // This should be the last check as it only disables AOTStubCaching - if ((_compressedOopBase == nullptr || CompressedOops::base() == nullptr) && (_compressedOopBase != CompressedOops::base())) { - log_debug(aot, codecache, init)("AOTStubCaching is disabled: incompatible CompressedOops::base(): %p vs current %p", _compressedOopBase, CompressedOops::base()); - AOTStubCaching = false; - } - return true; } -#undef TEST_FLAG - bool AOTCodeCache::Header::verify(uint load_size) const { if (_version != AOT_CODE_VERSION) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version); + load_failure_log().print_cr("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version); return false; } if (load_size < _cache_size) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size); + load_failure_log().print_cr("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size); return false; } return true; @@ -811,7 +637,11 @@ AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry) { _lookup_failed = false; _name = nullptr; _reloc_data = nullptr; + _reloc_count = 0; _oop_maps = nullptr; + _entry_kind = AOTCodeEntry::None; + _stub_data = nullptr; + _id = -1; } void AOTCodeReader::set_read_position(uint pos) { @@ -1029,6 +859,7 @@ bool AOTCodeCache::finish_write() { AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry uint adapters_count = 0; uint shared_blobs_count = 0; + uint stubgen_blobs_count = 0; uint C1_blobs_count = 0; uint C2_blobs_count = 0; uint max_size = 0; @@ -1056,6 +887,8 @@ bool AOTCodeCache::finish_write() { adapters_count++; } else if (kind == AOTCodeEntry::SharedBlob) { shared_blobs_count++; + } else if (kind == AOTCodeEntry::StubGenBlob) { + stubgen_blobs_count++; } else if (kind == AOTCodeEntry::C1Blob) { C1_blobs_count++; } else if (kind == AOTCodeEntry::C2Blob) { @@ -1092,6 +925,7 @@ bool AOTCodeCache::finish_write() { log_debug(aot, codecache, exit)(" Adapters: total=%u", adapters_count); log_debug(aot, codecache, exit)(" Shared Blobs: total=%d", shared_blobs_count); + log_debug(aot, codecache, exit)(" StubGen Blobs: total=%d", stubgen_blobs_count); log_debug(aot, codecache, exit)(" C1 Blobs: total=%d", C1_blobs_count); log_debug(aot, codecache, exit)(" C2 Blobs: total=%d", C2_blobs_count); log_debug(aot, codecache, exit)(" AOT code cache size: %u bytes, max entry's size: %u bytes", size, max_size); @@ -1101,7 +935,8 @@ bool AOTCodeCache::finish_write() { header->init(size, (uint)strings_count, strings_offset, entries_count, new_entries_offset, adapters_count, shared_blobs_count, - C1_blobs_count, C2_blobs_count, cpu_features_offset); + stubgen_blobs_count, C1_blobs_count, + C2_blobs_count, cpu_features_offset); log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count); } @@ -1110,19 +945,53 @@ bool AOTCodeCache::finish_write() { //------------------Store/Load AOT code ---------------------- -bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name, AOTStubData* stub_data, CodeBuffer* code_buffer) { + assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + + // we only expect stub data and a code buffer for a multi stub blob + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind) == (stub_data != nullptr), + "entry_kind %d does not match stub_data pointer %p", + entry_kind, stub_data); + + assert((stub_data == nullptr) == (code_buffer == nullptr), + "stub data and code buffer must both be null or both non null"); + + // If this is a stub and the cache is on for either load or dump we + // need to insert the stub entries into the AOTCacheAddressTable so + // that relocs which refer to entries defined by this blob get + // translated correctly. + // + // Entry insertion needs to be be done up front before writing the + // blob because some blobs rely on internal daisy-chain references + // from one entry to another. + // + // Entry insertion also needs to be done even if the cache is open + // for use but not for dump. This may be needed when an archived + // blob omits some entries -- either because of a config change or a + // load failure -- with the result that the entries end up being + // generated. These generated entry addresses may be needed to + // resolve references from subsequently loaded blobs (for either + // stubs or nmethods). + + if (is_on() && AOTCodeEntry::is_blob(entry_kind)) { + publish_stub_addresses(blob, (BlobId)id, stub_data); + } + AOTCodeCache* cache = open_for_dump(); if (cache == nullptr) { return false; } - assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); - if (AOTCodeEntry::is_adapter(entry_kind) && !is_dumping_adapter()) { return false; } if (AOTCodeEntry::is_blob(entry_kind) && !is_dumping_stub()) { return false; } + // we do not currently store C2 stubs because we are seeing weird + // memory errors when loading them -- see JDK-8357593 + if (entry_kind == AOTCodeEntry::C2Blob) { + return false; + } log_debug(aot, codecache, stubs)("Writing blob '%s' (id=%u, kind=%s) to AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]); #ifdef ASSERT @@ -1162,8 +1031,44 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind } CodeBlob::archive_blob(&blob, archive_buffer); - uint reloc_data_size = blob.relocation_size(); - n = cache->write_bytes((address)blob.relocation_begin(), reloc_data_size); + // For a relocatable code blob its relocations are linked from the + // blob. However, for a non-relocatable (stubgen) blob we only have + // transient relocations attached to the code buffer that are added + // in order to support AOT-load time patching. in either case, we + // need to explicitly save these relocs when storing the blob to the + // archive so we can then reload them and reattach them to either + // the blob or to a code buffer when we reload the blob into a + // production JVM. + // + // Either way we are then in a position to iterate over the relocs + // and AOT patch the ones that refer to code that may move between + // assembly and production time. We also need to save and restore + // AOT address table indexes for the target addresses of affected + // relocs. That happens below. + + int reloc_count; + address reloc_data; + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); + reloc_count = (cs->has_locs() ? cs->locs_count() : 0); + reloc_data = (reloc_count > 0 ? (address)cs->locs_start() : nullptr); + } else { + reloc_count = blob.relocation_size() / sizeof(relocInfo); + reloc_data = (address)blob.relocation_begin(); + } + n = cache->write_bytes(&reloc_count, sizeof(int)); + if (n != sizeof(int)) { + return false; + } + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + // align to heap word size before writing the relocs so we can + // install them into a code buffer when they get restored + if (!cache->align_write()) { + return false; + } + } + uint reloc_data_size = (uint)(reloc_count * sizeof(relocInfo)); + n = cache->write_bytes(reloc_data, reloc_data_size); if (n != reloc_data_size) { return false; } @@ -1176,7 +1081,29 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind has_oop_maps = true; } - if (!cache->write_relocations(blob)) { + // In the case of a multi-stub blob we need to write start, end, + // secondary entries and extras. For any other blob entry addresses + // beyond the blob start will be stored in the blob as offsets. + if (stub_data != nullptr) { + if (!cache->write_stub_data(blob, stub_data)) { + return false; + } + } + + // now we have added all the other data we can write details of any + // extra the AOT relocations + + bool write_ok; + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); + RelocIterator iter(cs); + write_ok = cache->write_relocations(blob, iter); + } else { + RelocIterator iter(&blob); + write_ok = cache->write_relocations(blob, iter); + } + + if (!write_ok) { if (!cache->failed()) { // We may miss an address in AOT table - skip this code blob. cache->set_write_position(entry_position); @@ -1195,6 +1122,7 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind #endif /* PRODUCT */ uint entry_size = cache->_write_position - entry_position; + AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id), entry_position, entry_size, name_offset, name_size, blob_offset, has_oop_maps, blob.content_begin()); @@ -1202,25 +1130,141 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind return true; } +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name) { + assert(!AOTCodeEntry::is_blob(entry_kind), + "wrong entry kind for numeric id %d", id); + return store_code_blob(blob, entry_kind, (uint)id, name, nullptr, nullptr); +} + bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) { - assert(AOTCodeEntry::is_blob(entry_kind), + assert(AOTCodeEntry::is_single_stub_blob(entry_kind), "wrong entry kind for blob id %s", StubInfo::name(id)); - return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id)); + return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), nullptr, nullptr); } -CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) { +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data, CodeBuffer* code_buffer) { + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind), + "wrong entry kind for multi stub blob id %s", StubInfo::name(id)); + return store_code_blob(blob, entry_kind, (uint)id, StubInfo::name(id), stub_data, code_buffer); +} + +bool AOTCodeCache::write_stub_data(CodeBlob &blob, AOTStubData *stub_data) { + BlobId blob_id = stub_data->blob_id(); + StubId stub_id = StubInfo::stub_base(blob_id); + address blob_base = blob.code_begin(); + int stub_cnt = StubInfo::stub_count(blob_id); + int n; + + LogStreamHandle(Trace, aot, codecache, stubs) log; + + if (log.is_enabled()) { + log.print_cr("======== Stub data starts at offset %d", _write_position); + } + + for (int i = 0; i < stub_cnt; i++, stub_id = StubInfo::next_in_blob(blob_id, stub_id)) { + // for each stub we find in the ranges list we write an int + // sequence where + // + // - start_pos is the stub start address encoded as a code section offset + // + // - end is the stub end address encoded as an offset from start + // + // - N counts the number of stub-local entries/extras + // + // - offseti is a stub-local entry/extra address encoded as len for + // a null address otherwise as an offset in range [1,len-1] + + StubAddrRange& range = stub_data->get_range(i); + GrowableArray
& addresses = stub_data->address_array(); + int base = range.start_index(); + if (base >= 0) { + n = write_bytes(&stub_id, sizeof(StubId)); + if (n != sizeof(StubId)) { + return false; + } + address start = addresses.at(base); + assert (blob_base <= start, "sanity"); + uint offset = (uint)(start - blob_base); + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + address end = addresses.at(base + 1); + assert (start < end, "sanity"); + offset = (uint)(end - start); + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + // write number of secondary and extra entries + int count = range.count() - 2; + n = write_bytes(&count, sizeof(int)); + if (n != sizeof(int)) { + return false; + } + for (int j = 0; j < count; j++) { + address next = addresses.at(base + 2 + j); + if (next != nullptr) { + // n.b. This maps next == end to the stub length which + // means we will reconstitute the address as nullptr. That + // happens when we have a handler range covers the end of + // a stub and needs to be handled specially by the client + // that restores the extras. + assert(start <= next && next <= end, "sanity"); + offset = (uint)(next - start); + } else { + // this can happen when a stub is not generated or an + // extra is the common handler target + offset = NULL_ADDRESS_MARKER; + } + n = write_bytes(&offset, sizeof(uint)); + if (n != sizeof(int)) { + return false; + } + } + if (log.is_enabled()) { + log.print_cr("======== wrote stub %s and %d addresses up to offset %d", + StubInfo::name(stub_id), range.count(), _write_position); + } + } + } + // we should have exhausted all stub ids in the blob + assert(stub_id == StubId::NO_STUBID, "sanity"); + // write NO_STUBID as an end marker + n = write_bytes(&stub_id, sizeof(StubId)); + if (n != sizeof(StubId)) { + return false; + } + + if (log.is_enabled()) { + log.print_cr("======== Stub data ends at offset %d", _write_position); + } + + return true; +} + +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name, AOTStubData* stub_data) { AOTCodeCache* cache = open_for_use(); if (cache == nullptr) { return nullptr; } assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind) == (stub_data != nullptr), + "entry_kind %d does not match stub_data pointer %p", + entry_kind, stub_data); + if (AOTCodeEntry::is_adapter(entry_kind) && !is_using_adapter()) { return nullptr; } if (AOTCodeEntry::is_blob(entry_kind) && !is_using_stub()) { return nullptr; } + // we do not currently load C2 stubs because we are seeing weird + // memory errors when loading them -- see JDK-8357593 + if (entry_kind == AOTCodeEntry::C2Blob) { + return nullptr; + } log_debug(aot, codecache, stubs)("Reading blob '%s' (id=%u, kind=%s) from AOT Code Cache", name, id, aot_code_entry_kind_name[entry_kind]); AOTCodeEntry* entry = cache->find_entry(entry_kind, encode_id(entry_kind, id)); @@ -1228,20 +1272,32 @@ CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, c return nullptr; } AOTCodeReader reader(cache, entry); - CodeBlob* blob = reader.compile_code_blob(name); + CodeBlob* blob = reader.compile_code_blob(name, entry_kind, id, stub_data); log_debug(aot, codecache, stubs)("%sRead blob '%s' (id=%u, kind=%s) from AOT Code Cache", (blob == nullptr? "Failed to " : ""), name, id, aot_code_entry_kind_name[entry_kind]); return blob; } +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name) { + assert(!AOTCodeEntry::is_blob(entry_kind), + "wrong entry kind for numeric id %d", id); + return load_code_blob(entry_kind, (uint)id, name, nullptr); +} + CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id) { - assert(AOTCodeEntry::is_blob(entry_kind), + assert(AOTCodeEntry::is_single_stub_blob(entry_kind), + "wrong entry kind for blob id %s", StubInfo::name(id)); + return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), nullptr); +} + +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, BlobId id, AOTStubData* stub_data) { + assert(AOTCodeEntry::is_multi_stub_blob(entry_kind), "wrong entry kind for blob id %s", StubInfo::name(id)); - return load_code_blob(entry_kind, (uint)id, StubInfo::name(id)); + return load_code_blob(entry_kind, (uint)id, StubInfo::name(id), stub_data); } -CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { +CodeBlob* AOTCodeReader::compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data) { uint entry_position = _entry->offset(); // Read name @@ -1257,20 +1313,32 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { } _name = stored_name; - // Read archived code blob + // Read archived code blob and related info uint offset = entry_position + _entry->blob_offset(); CodeBlob* archived_blob = (CodeBlob*)addr(offset); offset += archived_blob->size(); + _reloc_count = *(int*)addr(offset); offset += sizeof(int); + if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { + // position of relocs will have been aligned to heap word size so + // we can install them into a code buffer + offset = align_up(offset, DATA_ALIGNMENT); + } _reloc_data = (address)addr(offset); - offset += archived_blob->relocation_size(); + offset += _reloc_count * sizeof(relocInfo); set_read_position(offset); if (_entry->has_oop_maps()) { _oop_maps = read_oop_map_set(); } + // record current context for use by that callback + _stub_data = stub_data; + _entry_kind = entry_kind; + _id = id; + // CodeBlob::restore() calls AOTCodeReader::restore() + CodeBlob* code_blob = CodeBlob::create(archived_blob, this); if (code_blob == nullptr) { // no space left in CodeCache @@ -1293,10 +1361,60 @@ void AOTCodeReader::restore(CodeBlob* code_blob) { precond(_reloc_data != nullptr); code_blob->set_name(_name); - code_blob->restore_mutable_data(_reloc_data); + // Saved relocations need restoring except for the case of a + // multi-stub blob which has no runtime relocations. However, we may + // still have saved some (re-)load time relocs that were attached to + // the generator's code buffer. We don't attach them to the blob but + // they get processed below by fix_relocations. + if (!AOTCodeEntry::is_multi_stub_blob(_entry_kind)) { + code_blob->restore_mutable_data(_reloc_data); + } code_blob->set_oop_maps(_oop_maps); - fix_relocations(code_blob); + // if this is a multi stub blob load its entries + if (AOTCodeEntry::is_blob(_entry_kind)) { + BlobId blob_id = static_cast(_id); + if (StubInfo::is_stubgen(blob_id)) { + assert(_stub_data != nullptr, "sanity"); + read_stub_data(code_blob, _stub_data); + } + // publish entries found either in stub_data or as offsets in blob + AOTCodeCache::publish_stub_addresses(*code_blob, blob_id, _stub_data); + } + + // Now that all the entry points are in the address table we can + // read all the extra reloc info and fix up any addresses that need + // patching to adjust for a new location in a new JVM. We can be + // sure to correctly update all runtime references, including + // cross-linked stubs that are internally daisy-chained. If + // relocation fails and we have to re-generate any of the stubs then + // the entry points for newly generated stubs will get updated, + // ensuring that any other stubs or nmethods we need to relocate + // will use the correct address. + + // if we have a relocatable code blob then the relocs are already + // attached to the blob and we can iterate over it to find the ones + // we need to patch. With a non-relocatable code blob we need to + // wrap it with a CodeBuffer and then reattach the relocs to the + // code buffer. + + if (AOTCodeEntry::is_multi_stub_blob(_entry_kind)) { + // the blob doesn't have any proper runtime relocs but we can + // reinstate the AOT-load time relocs we saved from the code + // buffer that generated this blob in a new code buffer and use + // the latter to iterate over them + CodeBuffer code_buffer(code_blob); + relocInfo* locs = (relocInfo*)_reloc_data; + code_buffer.insts()->initialize_shared_locs(locs, _reloc_count); + code_buffer.insts()->set_locs_end(locs + _reloc_count); + CodeSection *cs = code_buffer.code_section(CodeBuffer::SECT_INSTS); + RelocIterator reloc_iter(cs); + fix_relocations(code_blob, reloc_iter); + } else { + // the AOT-load time relocs will be in the blob's restored relocs + RelocIterator reloc_iter(code_blob); + fix_relocations(code_blob, reloc_iter); + } #ifndef PRODUCT code_blob->asm_remarks().init(); @@ -1306,15 +1424,150 @@ void AOTCodeReader::restore(CodeBlob* code_blob) { #endif // PRODUCT } +void AOTCodeReader::read_stub_data(CodeBlob* code_blob, AOTStubData* stub_data) { + GrowableArray
& addresses = stub_data->address_array(); + // Read the list of stub ids and associated start, end, secondary + // and extra addresses and install them in the stub data. + // + // Also insert all start and secondary addresses into the AOTCache + // address table so we correctly relocate this blob and any followng + // blobs/nmethods. + // + // n.b. if an error occurs and we need to regenerate any of these + // stubs the address table will be updated as a side-effect of + // regeneration. + + address blob_base = code_blob->code_begin(); + uint blob_size = (uint)(code_blob->code_end() - blob_base); + int offset = read_position(); + LogStreamHandle(Trace, aot, codecache, stubs) log; + if (log.is_enabled()) { + log.print_cr("======== Stub data starts at offset %d", offset); + } + // read stub and entries until we see NO_STUBID + StubId stub_id = *(StubId*)addr(offset); offset += sizeof(StubId); + // we ought to have at least one saved stub in the blob + assert(stub_id != StubId::NO_STUBID, "blob %s contains no stubs!", StubInfo::name(stub_data->blob_id())); + while (stub_id != StubId::NO_STUBID) { + assert(StubInfo::blob(stub_id) == stub_data->blob_id(), "sanity"); + int idx = StubInfo::stubgen_offset_in_blob(stub_data->blob_id(), stub_id); + StubAddrRange& range = stub_data->get_range(idx); + // we should only see a stub once + assert(range.start_index() < 0, "repeated entry for stub %s", StubInfo::name(stub_id)); + int address_base = addresses.length(); + // start is an offset from the blob base + uint start = *(uint*)addr(offset); offset += sizeof(uint); + assert(start < blob_size, "stub %s start offset %d exceeds buffer length %d", StubInfo::name(stub_id), start, blob_size); + address stub_start = blob_base + start; + addresses.append(stub_start); + // end is an offset from the stub start + uint end = *(uint*)addr(offset); offset += sizeof(uint); + assert(start + end <= blob_size, "stub %s end offset %d exceeds remaining buffer length %d", StubInfo::name(stub_id), end, blob_size - start); + addresses.append(stub_start + end); + // read count of secondary entries plus extras + int entries_count = *(int*)addr(offset); offset += sizeof(int); + assert(entries_count >= (StubInfo::entry_count(stub_id) - 1), "not enough entries for %s", StubInfo::name(stub_id)); + for (int i = 0; i < entries_count; i++) { + // entry offset is an offset from the stub start less than or + // equal to end + uint entry = *(uint*)addr(offset); offset += sizeof(uint); + if (entry <= end) { + // entry addresses may not address end but extras can + assert(entry < end || i >= StubInfo::entry_count(stub_id), + "entry offset 0x%x exceeds stub length 0x%x for stub %s", + entry, end, StubInfo::name(stub_id)); + addresses.append(stub_start + entry); + } else { + // special case: entry encodes a nullptr + assert(entry == AOTCodeCache::NULL_ADDRESS_MARKER, "stub %s entry offset %d lies beyond stub end %d and does not equal NULL_ADDRESS_MARKER", StubInfo::name(stub_id), entry, end); + addresses.append(nullptr); + } + } + if (log.is_enabled()) { + log.print_cr("======== read stub %s and %d addresses up to offset %d", + StubInfo::name(stub_id), 2 + entries_count, offset); + } + range.init_entry(address_base, 2 + entries_count); + // move on to next stub or NO_STUBID + stub_id = *(StubId*)addr(offset); offset += sizeof(StubId); + } + if (log.is_enabled()) { + log.print_cr("======== Stub data ends at offset %d", offset); + } + + set_read_position(offset); +} + +void AOTCodeCache::publish_external_addresses(GrowableArray
& addresses) { + DEBUG_ONLY( _passed_init2 = true; ) + if (opened_cache == nullptr) { + return; + } + + cache()->_table->add_external_addresses(addresses); +} + +void AOTCodeCache::publish_stub_addresses(CodeBlob &code_blob, BlobId blob_id, AOTStubData *stub_data) { + if (stub_data != nullptr) { + // register all entries in stub + assert(StubInfo::stub_count(blob_id) > 1, + "multiple stub data provided for single stub blob %s", + StubInfo::name(blob_id)); + assert(blob_id == stub_data->blob_id(), + "blob id %s does not match id in stub data %s", + StubInfo::name(blob_id), + StubInfo::name(stub_data->blob_id())); + // iterate over all stubs in the blob + StubId stub_id = StubInfo::stub_base(blob_id); + int stub_cnt = StubInfo::stub_count(blob_id); + GrowableArray
& addresses = stub_data->address_array(); + for (int i = 0; i < stub_cnt; i++) { + assert(stub_id != StubId::NO_STUBID, "sanity"); + StubAddrRange& range = stub_data->get_range(i); + int base = range.start_index(); + if (base >= 0) { + cache()->add_stub_entries(stub_id, addresses.at(base), &addresses, base + 2); + } + stub_id = StubInfo::next_in_blob(blob_id, stub_id); + } + // we should have exhausted all stub ids in the blob + assert(stub_id == StubId::NO_STUBID, "sanity"); + } else { + // register entry or entries for a single stub blob + StubId stub_id = StubInfo::stub_base(blob_id); + assert(StubInfo::stub_count(blob_id) == 1, + "multiple stub blob %s provided without stub data", + StubInfo::name(blob_id)); + address start = code_blob.code_begin(); + if (StubInfo::entry_count(stub_id) == 1) { + assert(!code_blob.is_deoptimization_stub(), "expecting multiple entries for stub %s", StubInfo::name(stub_id)); + // register the blob base address as the only entry + cache()->add_stub_entries(stub_id, start); + } else { + assert(code_blob.is_deoptimization_stub(), "only expecting one entry for stub %s", StubInfo::name(stub_id)); + DeoptimizationBlob *deopt_blob = code_blob.as_deoptimization_blob(); + assert(deopt_blob->unpack() == start, "unexpected offset 0x%x for deopt stub entry", (int)(deopt_blob->unpack() - start)); + GrowableArray
addresses; + addresses.append(deopt_blob->unpack_with_exception()); + addresses.append(deopt_blob->unpack_with_reexecution()); + addresses.append(deopt_blob->unpack_with_exception_in_tls()); +#if INCLUDE_JVMCI + addresses.append(deopt_blob->uncommon_trap()); + addresses.append(deopt_blob->implicit_exception_uncommon_trap()); +#endif // INCLUDE_JVMCI + cache()->add_stub_entries(stub_id, start, &addresses, 0); + } + } +} + // ------------ process code and data -------------- // Can't use -1. It is valid value for jump to iteself destination // used by static call stub: see NativeJump::jump_destination(). #define BAD_ADDRESS_ID -2 -bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { +bool AOTCodeCache::write_relocations(CodeBlob& code_blob, RelocIterator& iter) { GrowableArray reloc_data; - RelocIterator iter(&code_blob); LogStreamHandle(Trace, aot, codecache, reloc) log; while (iter.next()) { int idx = reloc_data.append(0); // default value @@ -1368,6 +1621,11 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { // Write the count first int count = reloc_data.length(); write_bytes(&count, sizeof(int)); + if (log.is_enabled()) { + log.print_cr("======== extra relocations count=%d", count); + log.print( " {"); + } + bool first = true; for (GrowableArrayIterator iter = reloc_data.begin(); iter != reloc_data.end(); ++iter) { uint value = *iter; @@ -1375,23 +1633,43 @@ bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { if (n != sizeof(uint)) { return false; } + if (log.is_enabled()) { + if (first) { + first = false; + log.print("%d", value); + } else { + log.print(", %d", value); + } + } + } + if (log.is_enabled()) { + log.print_cr("}"); } return true; } -void AOTCodeReader::fix_relocations(CodeBlob* code_blob) { - LogStreamHandle(Trace, aot, reloc) log; +void AOTCodeReader::fix_relocations(CodeBlob *code_blob, RelocIterator& iter) { uint offset = read_position(); - int count = *(int*)addr(offset); + int reloc_count = *(int*)addr(offset); offset += sizeof(int); - if (log.is_enabled()) { - log.print_cr("======== extra relocations count=%d", count); - } uint* reloc_data = (uint*)addr(offset); - offset += (count * sizeof(uint)); + offset += (reloc_count * sizeof(uint)); set_read_position(offset); - RelocIterator iter(code_blob); + LogStreamHandle(Trace, aot, codecache, reloc) log; + if (log.is_enabled()) { + log.print_cr("======== extra relocations count=%d", reloc_count); + log.print(" {"); + for(int i = 0; i < reloc_count; i++) { + if (i == 0) { + log.print("%d", reloc_data[i]); + } else { + log.print(", %d", reloc_data[i]); + } + } + log.print_cr("}"); + } + int j = 0; while (iter.next()) { switch (iter.type()) { @@ -1440,7 +1718,7 @@ void AOTCodeReader::fix_relocations(CodeBlob* code_blob) { } j++; } - assert(j == count, "sanity"); + assert(j == reloc_count, "sanity"); } bool AOTCodeCache::write_oop_map_set(CodeBlob& cb) { @@ -1550,249 +1828,360 @@ void AOTCodeReader::read_dbg_strings(DbgStrings& dbg_strings) { //======================= AOTCodeAddressTable =============== -// address table ids for generated routines, external addresses and C -// string addresses are partitioned into positive integer ranges -// defined by the following positive base and max values -// i.e. [_extrs_base, _extrs_base + _extrs_max -1], -// [_blobs_base, _blobs_base + _blobs_max -1], -// ... -// [_c_str_base, _c_str_base + _c_str_max -1], +// address table ids for generated routine entry adresses, external +// addresses and C string addresses are partitioned into positive +// integer ranges defined by the following positive base and max +// values i.e. [_extrs_base, _extrs_base + _extrs_max -1], +// [_stubs_base, _stubs_base + _stubs_max -1], [_c_str_base, +// _c_str_base + _c_str_max -1], -#define _extrs_max 100 -#define _stubs_max 3 - -#define _shared_blobs_max 20 -#define _C1_blobs_max 10 -#define _blobs_max (_shared_blobs_max+_C1_blobs_max) -#define _all_max (_extrs_max+_stubs_max+_blobs_max) +#define _extrs_max 380 +#define _stubs_max static_cast(EntryId::NUM_ENTRYIDS) #define _extrs_base 0 #define _stubs_base (_extrs_base + _extrs_max) -#define _shared_blobs_base (_stubs_base + _stubs_max) -#define _C1_blobs_base (_shared_blobs_base + _shared_blobs_max) -#define _blobs_end (_shared_blobs_base + _blobs_max) +#define _all_max (_stubs_base + _stubs_max) + +// setter for external addresses and string addresses inserts new +// addresses in the order they are encountered them which must remain +// the same across an assembly run and subsequent production run -#define SET_ADDRESS(type, addr) \ - { \ - type##_addr[type##_length++] = (address) (addr); \ - assert(type##_length <= type##_max, "increase size"); \ +#define ADD_EXTERNAL_ADDRESS(addr) \ + { \ + hash_address((address) addr, _extrs_base + _extrs_length); \ + _extrs_addr[_extrs_length++] = (address) (addr); \ + assert(_extrs_length <= _extrs_max, "increase size"); \ } +// insert into to the address hash table the index of an external +// address or a stub address in the list of external or stub +// addresses, respectively, keyed by the relevant address + +void AOTCodeAddressTable::hash_address(address addr, int idx) { + // only do this if we are caching stubs and we have a non-null + // address to record + if (!AOTStubCaching) { + return; + } + if (addr == nullptr) { + return; + } + // check opened_cache because this can be called before the cache is + // properly initialized and only continue when dumping is enabled + if (opened_cache != nullptr && opened_cache->for_dump()) { + if (_hash_table == nullptr) { + _hash_table = new (mtCode) AOTCodeAddressHashTable(); + } + assert(_hash_table->get(addr) == nullptr, "repeated insert of address " INTPTR_FORMAT, p2i(addr)); + _hash_table->put(addr, idx); + log_trace(aot, codecache)("Address " INTPTR_FORMAT " inserted into AOT Code Cache address hash table with index '%d'", + p2i(addr), idx); + } +} + static bool initializing_extrs = false; void AOTCodeAddressTable::init_extrs() { if (_extrs_complete || initializing_extrs) return; // Done already - assert(_blobs_end <= _all_max, "AOTCodeAddress table ranges need adjusting"); - initializing_extrs = true; _extrs_addr = NEW_C_HEAP_ARRAY(address, _extrs_max, mtCode); _extrs_length = 0; + { + // Required by initial stubs + ADD_EXTERNAL_ADDRESS(SharedRuntime::exception_handler_for_return_address); // used by forward_exception + ADD_EXTERNAL_ADDRESS(CompressedOops::base_addr()); // used by call_stub + ADD_EXTERNAL_ADDRESS(Thread::current); // used by call_stub + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_StackOverflowError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError); + } + // Record addresses of VM runtime methods - SET_ADDRESS(_extrs, SharedRuntime::fixup_callers_callsite); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_abstract); - SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_ic_miss); + ADD_EXTERNAL_ADDRESS(SharedRuntime::fixup_callers_callsite); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method_abstract); + ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method_ic_miss); #if defined(AARCH64) && !defined(ZERO) - SET_ADDRESS(_extrs, JavaThread::aarch64_get_thread_helper); + ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper); +#endif + +#if defined(AARCH64) + ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr()); +#endif + +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jbyte_array_copy_ctr); // used by arraycopy stub on arm32 and x86_64 + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jshort_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jint_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_jlong_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_oop_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_checkcast_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_unsafe_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_generic_array_copy_ctr); // used by arraycopy stub + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_unsafe_set_memory_ctr); // used by arraycopy stub +#endif /* PRODUCT */ + + ADD_EXTERNAL_ADDRESS(SharedRuntime::enable_stack_reserved_zone); + +#if defined(AMD64) && !defined(ZERO) + ADD_EXTERNAL_ADDRESS(SharedRuntime::montgomery_multiply); + ADD_EXTERNAL_ADDRESS(SharedRuntime::montgomery_square); +#endif // defined(AMD64) && !defined(ZERO) + + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2f); + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2i); + ADD_EXTERNAL_ADDRESS(SharedRuntime::d2l); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dcos); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dexp); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dlog10); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dpow); +#ifndef ZERO + ADD_EXTERNAL_ADDRESS(SharedRuntime::drem); #endif + ADD_EXTERNAL_ADDRESS(SharedRuntime::dsin); + ADD_EXTERNAL_ADDRESS(SharedRuntime::dtan); + ADD_EXTERNAL_ADDRESS(SharedRuntime::f2i); + ADD_EXTERNAL_ADDRESS(SharedRuntime::f2l); +#ifndef ZERO + ADD_EXTERNAL_ADDRESS(SharedRuntime::frem); +#endif + ADD_EXTERNAL_ADDRESS(SharedRuntime::l2d); + ADD_EXTERNAL_ADDRESS(SharedRuntime::l2f); + ADD_EXTERNAL_ADDRESS(SharedRuntime::ldiv); + ADD_EXTERNAL_ADDRESS(SharedRuntime::lmul); + ADD_EXTERNAL_ADDRESS(SharedRuntime::lrem); + +#if INCLUDE_JVMTI + ADD_EXTERNAL_ADDRESS(&JvmtiExport::_should_notify_object_alloc); +#endif /* INCLUDE_JVMTI */ + + ADD_EXTERNAL_ADDRESS(ThreadIdentifier::unsafe_offset()); + // already added + // ADD_EXTERNAL_ADDRESS(Thread::current); + + ADD_EXTERNAL_ADDRESS(os::javaTimeMillis); + ADD_EXTERNAL_ADDRESS(os::javaTimeNanos); +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(os::breakpoint); +#endif + + ADD_EXTERNAL_ADDRESS(StubRoutines::crc_table_addr()); +#ifndef PRODUCT + ADD_EXTERNAL_ADDRESS(&SharedRuntime::_partial_subtype_ctr); +#endif + +#if INCLUDE_JFR + ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::write_checkpoint); + ADD_EXTERNAL_ADDRESS(JfrIntrinsicSupport::return_lease); +#endif + + ADD_EXTERNAL_ADDRESS(UpcallLinker::handle_uncaught_exception); // used by upcall_stub_exception_handler + { // Required by Shared blobs - SET_ADDRESS(_extrs, Deoptimization::fetch_unroll_info); - SET_ADDRESS(_extrs, Deoptimization::unpack_frames); - SET_ADDRESS(_extrs, SafepointSynchronize::handle_polling_page_exception); - SET_ADDRESS(_extrs, SharedRuntime::resolve_opt_virtual_call_C); - SET_ADDRESS(_extrs, SharedRuntime::resolve_virtual_call_C); - SET_ADDRESS(_extrs, SharedRuntime::resolve_static_call_C); - SET_ADDRESS(_extrs, SharedRuntime::throw_StackOverflowError); - SET_ADDRESS(_extrs, SharedRuntime::throw_delayed_StackOverflowError); - SET_ADDRESS(_extrs, SharedRuntime::throw_AbstractMethodError); - SET_ADDRESS(_extrs, SharedRuntime::throw_IncompatibleClassChangeError); - SET_ADDRESS(_extrs, SharedRuntime::throw_NullPointerException_at_call); + ADD_EXTERNAL_ADDRESS(Deoptimization::fetch_unroll_info); + ADD_EXTERNAL_ADDRESS(Deoptimization::unpack_frames); + ADD_EXTERNAL_ADDRESS(SafepointSynchronize::handle_polling_page_exception); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_opt_virtual_call_C); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_virtual_call_C); + ADD_EXTERNAL_ADDRESS(SharedRuntime::resolve_static_call_C); + // already added + // ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_delayed_StackOverflowError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_AbstractMethodError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_IncompatibleClassChangeError); + ADD_EXTERNAL_ADDRESS(SharedRuntime::throw_NullPointerException_at_call); } #ifdef COMPILER1 { // Required by C1 blobs - SET_ADDRESS(_extrs, static_cast(SharedRuntime::dtrace_object_alloc)); - SET_ADDRESS(_extrs, SharedRuntime::exception_handler_for_return_address); - SET_ADDRESS(_extrs, SharedRuntime::register_finalizer); - SET_ADDRESS(_extrs, Runtime1::is_instance_of); - SET_ADDRESS(_extrs, Runtime1::exception_handler_for_pc); - SET_ADDRESS(_extrs, Runtime1::check_abort_on_vm_exception); - SET_ADDRESS(_extrs, Runtime1::new_instance); - SET_ADDRESS(_extrs, Runtime1::counter_overflow); - SET_ADDRESS(_extrs, Runtime1::new_type_array); - SET_ADDRESS(_extrs, Runtime1::new_object_array); - SET_ADDRESS(_extrs, Runtime1::new_multi_array); - SET_ADDRESS(_extrs, Runtime1::throw_range_check_exception); - SET_ADDRESS(_extrs, Runtime1::throw_index_exception); - SET_ADDRESS(_extrs, Runtime1::throw_div0_exception); - SET_ADDRESS(_extrs, Runtime1::throw_null_pointer_exception); - SET_ADDRESS(_extrs, Runtime1::throw_array_store_exception); - SET_ADDRESS(_extrs, Runtime1::throw_class_cast_exception); - SET_ADDRESS(_extrs, Runtime1::throw_incompatible_class_change_error); - SET_ADDRESS(_extrs, Runtime1::is_instance_of); - SET_ADDRESS(_extrs, Runtime1::monitorenter); - SET_ADDRESS(_extrs, Runtime1::monitorexit); - SET_ADDRESS(_extrs, Runtime1::deoptimize); - SET_ADDRESS(_extrs, Runtime1::access_field_patching); - SET_ADDRESS(_extrs, Runtime1::move_klass_patching); - SET_ADDRESS(_extrs, Runtime1::move_mirror_patching); - SET_ADDRESS(_extrs, Runtime1::move_appendix_patching); - SET_ADDRESS(_extrs, Runtime1::predicate_failed_trap); - SET_ADDRESS(_extrs, Runtime1::unimplemented_entry); - SET_ADDRESS(_extrs, Thread::current); - SET_ADDRESS(_extrs, CompressedKlassPointers::base_addr()); -#ifndef PRODUCT - SET_ADDRESS(_extrs, os::breakpoint); -#endif + ADD_EXTERNAL_ADDRESS(static_cast(SharedRuntime::dtrace_object_alloc)); + ADD_EXTERNAL_ADDRESS(SharedRuntime::register_finalizer); + ADD_EXTERNAL_ADDRESS(Runtime1::is_instance_of); + ADD_EXTERNAL_ADDRESS(Runtime1::exception_handler_for_pc); + ADD_EXTERNAL_ADDRESS(Runtime1::check_abort_on_vm_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::new_instance); + ADD_EXTERNAL_ADDRESS(Runtime1::counter_overflow); + ADD_EXTERNAL_ADDRESS(Runtime1::new_type_array); + ADD_EXTERNAL_ADDRESS(Runtime1::new_object_array); + ADD_EXTERNAL_ADDRESS(Runtime1::new_multi_array); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_range_check_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_index_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_div0_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_null_pointer_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_array_store_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_class_cast_exception); + ADD_EXTERNAL_ADDRESS(Runtime1::throw_incompatible_class_change_error); + ADD_EXTERNAL_ADDRESS(Runtime1::monitorenter); + ADD_EXTERNAL_ADDRESS(Runtime1::monitorexit); + ADD_EXTERNAL_ADDRESS(Runtime1::deoptimize); + ADD_EXTERNAL_ADDRESS(Runtime1::access_field_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_klass_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_mirror_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::move_appendix_patching); + ADD_EXTERNAL_ADDRESS(Runtime1::predicate_failed_trap); + ADD_EXTERNAL_ADDRESS(Runtime1::unimplemented_entry); + // already added + // ADD_EXTERNAL_ADDRESS(Thread::current); + ADD_EXTERNAL_ADDRESS(CompressedKlassPointers::base_addr()); } #endif #ifdef COMPILER2 { // Required by C2 blobs - SET_ADDRESS(_extrs, Deoptimization::uncommon_trap); - SET_ADDRESS(_extrs, OptoRuntime::handle_exception_C); - SET_ADDRESS(_extrs, OptoRuntime::new_instance_C); - SET_ADDRESS(_extrs, OptoRuntime::new_array_C); - SET_ADDRESS(_extrs, OptoRuntime::new_array_nozero_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray2_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray3_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray4_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarray5_C); - SET_ADDRESS(_extrs, OptoRuntime::multianewarrayN_C); - SET_ADDRESS(_extrs, OptoRuntime::complete_monitor_locking_C); - SET_ADDRESS(_extrs, OptoRuntime::monitor_notify_C); - SET_ADDRESS(_extrs, OptoRuntime::monitor_notifyAll_C); - SET_ADDRESS(_extrs, OptoRuntime::rethrow_C); - SET_ADDRESS(_extrs, OptoRuntime::slow_arraycopy_C); - SET_ADDRESS(_extrs, OptoRuntime::register_finalizer_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_end_first_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_start_final_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_start_transition_C); - SET_ADDRESS(_extrs, OptoRuntime::vthread_end_transition_C); -#if defined(AARCH64) - SET_ADDRESS(_extrs, JavaThread::verify_cross_modify_fence_failure); -#endif // AARCH64 + ADD_EXTERNAL_ADDRESS(Deoptimization::uncommon_trap); + ADD_EXTERNAL_ADDRESS(OptoRuntime::handle_exception_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_instance_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_array_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::new_array_nozero_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray2_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray3_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray4_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarray5_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::multianewarrayN_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::complete_monitor_locking_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::monitor_notify_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::monitor_notifyAll_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::rethrow_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::slow_arraycopy_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::register_finalizer_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_first_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_final_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_start_transition_C); + ADD_EXTERNAL_ADDRESS(OptoRuntime::vthread_end_transition_C); + // already added for +#if defined(AARCH64) && ! defined(PRODUCT) + ADD_EXTERNAL_ADDRESS(JavaThread::verify_cross_modify_fence_failure); +#endif // AARCH64 && !PRODUCT } #endif // COMPILER2 #if INCLUDE_G1GC - SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry); + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_field_pre_entry); + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_narrow_oop_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_pre_oop_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(G1BarrierSetRuntime::write_ref_array_post_entry); // used by arraycopy stubs + ADD_EXTERNAL_ADDRESS(BarrierSetNMethod::nmethod_stub_entry_barrier); // used by method_entry_barrier + #endif #if INCLUDE_SHENANDOAHGC - SET_ADDRESS(_extrs, ShenandoahRuntime::write_barrier_pre); - SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom); - SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_strong_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_weak_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::load_reference_barrier_phantom_narrow); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_oop); + ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::arraycopy_barrier_narrow_oop); #endif #if INCLUDE_ZGC - SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); - SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_weak_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr()); + ADD_EXTERNAL_ADDRESS(ZBarrierSetRuntime::load_barrier_on_oop_array_addr()); + + ADD_EXTERNAL_ADDRESS(ZPointerVectorLoadBadMask); + ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreBadMask); + ADD_EXTERNAL_ADDRESS(ZPointerVectorStoreGoodMask); #if defined(AMD64) - SET_ADDRESS(_extrs, &ZPointerLoadShift); + ADD_EXTERNAL_ADDRESS(&ZPointerLoadShift); + ADD_EXTERNAL_ADDRESS(&ZPointerLoadShiftTable); #endif #endif #ifndef ZERO #if defined(AMD64) || defined(AARCH64) || defined(RISCV64) - SET_ADDRESS(_extrs, MacroAssembler::debug64); -#endif + ADD_EXTERNAL_ADDRESS(MacroAssembler::debug64); +#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64) +#if defined(AMD64) + ADD_EXTERNAL_ADDRESS(warning); +#endif // defined(AMD64) #endif // ZERO // addresses of fields in AOT runtime constants area address* p = AOTRuntimeConstants::field_addresses_list(); while (*p != nullptr) { - SET_ADDRESS(_extrs, *p++); + address to_add = (address)*p++; + ADD_EXTERNAL_ADDRESS(to_add); } - _extrs_complete = true; - log_debug(aot, codecache, init)("External addresses recorded"); + log_debug(aot, codecache, init)("External addresses opened and recorded"); + // allocate storage for stub entries + _stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode); + log_debug(aot, codecache, init)("Stub addresses opened"); } -static bool initializing_early_stubs = false; - -void AOTCodeAddressTable::init_early_stubs() { - if (_complete || initializing_early_stubs) return; // Done already - initializing_early_stubs = true; - _stubs_addr = NEW_C_HEAP_ARRAY(address, _stubs_max, mtCode); - _stubs_length = 0; - SET_ADDRESS(_stubs, StubRoutines::forward_exception_entry()); +void AOTCodeAddressTable::init_extrs2() { + assert(initializing_extrs && !_extrs_complete, + "invalid sequence for init_extrs2"); { - // Required by C1 blobs -#if defined(AMD64) && !defined(ZERO) - SET_ADDRESS(_stubs, StubRoutines::x86::double_sign_flip()); - SET_ADDRESS(_stubs, StubRoutines::x86::d2l_fixup()); -#endif // AMD64 + ADD_EXTERNAL_ADDRESS(Continuation::prepare_thaw); // used by cont_thaw + ADD_EXTERNAL_ADDRESS(Continuation::thaw_entry()); // used by cont_thaw + ADD_EXTERNAL_ADDRESS(ContinuationEntry::thaw_call_pc_address()); // used by cont_preempt_stub } - - _early_stubs_complete = true; - log_info(aot, codecache, init)("Early stubs recorded"); + _extrs_complete = true; + initializing_extrs = false; + log_debug(aot, codecache, init)("External addresses recorded and closed"); } -static bool initializing_shared_blobs = false; - -void AOTCodeAddressTable::init_shared_blobs() { - if (_complete || initializing_shared_blobs) return; // Done already - initializing_shared_blobs = true; - address* blobs_addr = NEW_C_HEAP_ARRAY(address, _blobs_max, mtCode); - - // Divide _shared_blobs_addr array to chunks because they could be initialized in parrallel - _shared_blobs_addr = blobs_addr; - _C1_blobs_addr = _shared_blobs_addr + _shared_blobs_max; - - _shared_blobs_length = 0; - _C1_blobs_length = 0; +void AOTCodeAddressTable::add_external_addresses(GrowableArray
& addresses) { + assert(initializing_extrs && !_extrs_complete, + "invalid sequence for add_external_addresses"); + for (int i = 0; i < addresses.length(); i++) { + ADD_EXTERNAL_ADDRESS(addresses.at(i)); + } + log_debug(aot, codecache, init)("Recorded %d additional external addresses", + addresses.length()); +} - // clear the address table - memset(blobs_addr, 0, sizeof(address)* _blobs_max); +void AOTCodeAddressTable::add_stub_entry(EntryId entry_id, address a) { + assert(_extrs_complete || initializing_extrs, + "recording stub entry address before external addresses complete"); + assert(!(StubInfo::is_shared(StubInfo::stub(entry_id)) && _shared_stubs_complete), "too late to add shared entry"); + assert(!(StubInfo::is_stubgen(StubInfo::stub(entry_id)) && _stubgen_stubs_complete), "too late to add stubgen entry"); + assert(!(StubInfo::is_c1(StubInfo::stub(entry_id)) && _c1_stubs_complete), "too late to add c1 entry"); + assert(!(StubInfo::is_c2(StubInfo::stub(entry_id)) && _c2_stubs_complete), "too late to add c2 entry"); + log_debug(aot, stubs)("Recording address 0x%p for %s entry %s", a, StubInfo::name(StubInfo::stubgroup(entry_id)), StubInfo::name(entry_id)); + int idx = static_cast(entry_id); + hash_address(a, _stubs_base + idx); + _stubs_addr[idx] = a; +} - // Record addresses of generated code blobs - SET_ADDRESS(_shared_blobs, SharedRuntime::get_handle_wrong_method_stub()); - SET_ADDRESS(_shared_blobs, SharedRuntime::get_ic_miss_stub()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_exception()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_reexecution()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->unpack_with_exception_in_tls()); -#if INCLUDE_JVMCI - if (EnableJVMCI) { - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->uncommon_trap()); - SET_ADDRESS(_shared_blobs, SharedRuntime::deopt_blob()->implicit_exception_uncommon_trap()); - } -#endif +void AOTCodeAddressTable::set_shared_stubs_complete() { + assert(!_shared_stubs_complete, "repeated close for shared stubs!"); + _shared_stubs_complete = true; + log_debug(aot, codecache, init)("Shared stubs closed"); +} - _shared_blobs_complete = true; - log_debug(aot, codecache, init)("Early shared blobs recorded"); - _complete = true; +void AOTCodeAddressTable::set_c1_stubs_complete() { + assert(!_c1_stubs_complete, "repeated close for c1 stubs!"); + _c1_stubs_complete = true; + log_debug(aot, codecache, init)("C1 stubs closed"); } -void AOTCodeAddressTable::init_early_c1() { -#ifdef COMPILER1 - // Runtime1 Blobs - StubId id = StubInfo::stub_base(StubGroup::C1); - // include forward_exception in range we publish - StubId limit = StubInfo::next(StubId::c1_forward_exception_id); - for (; id != limit; id = StubInfo::next(id)) { - if (Runtime1::blob_for(id) == nullptr) { - log_info(aot, codecache, init)("C1 blob %s is missing", Runtime1::name_for(id)); - continue; - } - if (Runtime1::entry_for(id) == nullptr) { - log_info(aot, codecache, init)("C1 blob %s is missing entry", Runtime1::name_for(id)); - continue; - } - address entry = Runtime1::entry_for(id); - SET_ADDRESS(_C1_blobs, entry); - } -#endif // COMPILER1 - assert(_C1_blobs_length <= _C1_blobs_max, "increase _C1_blobs_max to %d", _C1_blobs_length); - _early_c1_complete = true; +void AOTCodeAddressTable::set_c2_stubs_complete() { + assert(!_c2_stubs_complete, "repeated close for c2 stubs!"); + _c2_stubs_complete = true; + log_debug(aot, codecache, init)("C2 stubs closed"); } -#undef SET_ADDRESS +void AOTCodeAddressTable::set_stubgen_stubs_complete() { + assert(!_stubgen_stubs_complete, "repeated close for stubgen stubs!"); + _stubgen_stubs_complete = true; + log_debug(aot, codecache, init)("StubGen stubs closed"); +} #ifdef PRODUCT #define MAX_STR_COUNT 200 @@ -1830,6 +2219,7 @@ void AOTCodeCache::load_strings() { uint len = string_lengths[i]; _C_strings_s[i] = i; _C_strings_id[i] = i; + log_trace(aot, codecache, stringtable)("load_strings: _C_strings[%d] " INTPTR_FORMAT " '%s'", i, p2i(p), p); p += len; } assert((uint)(p - _C_strings_buf) <= strings_size, "(" INTPTR_FORMAT " - " INTPTR_FORMAT ") = %d > %d ", p2i(p), p2i(_C_strings_buf), (uint)(p - _C_strings_buf), strings_size); @@ -1849,6 +2239,7 @@ int AOTCodeCache::store_strings() { } for (int i = 0; i < _C_strings_used; i++) { const char* str = _C_strings[_C_strings_s[i]]; + log_trace(aot, codecache, stringtable)("store_strings: _C_strings[%d] " INTPTR_FORMAT " '%s'", i, p2i(str), str); uint len = (uint)strlen(str) + 1; length += len; assert(len < 1000, "big string: %s", str); @@ -1876,7 +2267,7 @@ const char* AOTCodeCache::add_C_string(const char* str) { } const char* AOTCodeAddressTable::add_C_string(const char* str) { - if (_extrs_complete) { + if (_extrs_complete || initializing_extrs) { // Check previous strings address for (int i = 0; i < _C_strings_count; i++) { if (_C_strings_in[i] == str) { @@ -1914,6 +2305,7 @@ int AOTCodeAddressTable::id_for_C_string(address str) { assert(id < _C_strings_used, "%d >= %d", id , _C_strings_used); return id; // Found recorded } + log_trace(aot, codecache, stringtable)("id_for_C_string: _C_strings[%d ==> %d] " INTPTR_FORMAT " '%s'", i, _C_strings_used, p2i(str), str); // Not found in recorded, add new id = _C_strings_used++; _C_strings_s[id] = i; @@ -1939,7 +2331,7 @@ static int search_address(address addr, address* table, uint length) { } address AOTCodeAddressTable::address_for_id(int idx) { - assert(_extrs_complete, "AOT Code Cache VM runtime addresses table is not complete"); + assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete"); if (idx == -1) { return (address)-1; } @@ -1956,15 +2348,9 @@ address AOTCodeAddressTable::address_for_id(int idx) { if (/* id >= _extrs_base && */ id < _extrs_length) { return _extrs_addr[id - _extrs_base]; } - if (id >= _stubs_base && id < _stubs_base + _stubs_length) { + if (id >= _stubs_base && id < _c_str_base) { return _stubs_addr[id - _stubs_base]; } - if (id >= _shared_blobs_base && id < _shared_blobs_base + _shared_blobs_length) { - return _shared_blobs_addr[id - _shared_blobs_base]; - } - if (id >= _C1_blobs_base && id < _C1_blobs_base + _C1_blobs_length) { - return _C1_blobs_addr[id - _C1_blobs_base]; - } if (id >= _c_str_base && id < (_c_str_base + (uint)_C_strings_count)) { return address_for_C_string(id - _c_str_base); } @@ -1973,7 +2359,7 @@ address AOTCodeAddressTable::address_for_id(int idx) { } int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBlob* code_blob) { - assert(_extrs_complete, "AOT Code Cache VM runtime addresses table is not complete"); + assert(_extrs_complete || initializing_extrs, "AOT Code Cache VM runtime addresses table is not complete"); int id = -1; if (addr == (address)-1) { // Static call stub has jump to itself return id; @@ -1982,15 +2368,24 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB BarrierSet* bs = BarrierSet::barrier_set(); bool is_const_card_table_base = !UseG1GC && !UseShenandoahGC && bs->is_a(BarrierSet::CardTableBarrierSet); guarantee(!is_const_card_table_base || addr != ci_card_table_address_const(), "sanity"); - + // fast path for stubs and external addresses + if (_hash_table != nullptr) { + int *result = _hash_table->get(addr); + if (result != nullptr) { + id = *result; + log_trace(aot, codecache)("Address " INTPTR_FORMAT " retrieved from AOT Code Cache address hash table with index '%d'", + p2i(addr), id); + return id; + } + } // Seach for C string id = id_for_C_string(addr); if (id >= 0) { return id + _c_str_base; } - if (StubRoutines::contains(addr)) { - // Search in stubs - id = search_address(addr, _stubs_addr, _stubs_length); + if (StubRoutines::contains(addr) || CodeCache::find_blob(addr) != nullptr) { + // Search for a matching stub entry + id = search_address(addr, _stubs_addr, _stubs_max); if (id < 0) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); if (desc == nullptr) { @@ -2002,51 +2397,39 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB return id + _stubs_base; } } else { - CodeBlob* cb = CodeCache::find_blob(addr); - if (cb != nullptr) { - // Search in code blobs - int id_base = _shared_blobs_base; - id = search_address(addr, _shared_blobs_addr, _blobs_max); - if (id < 0) { - assert(false, "Address " INTPTR_FORMAT " for Blob:%s is missing in AOT Code Cache addresses table", p2i(addr), cb->name()); - } else { - return id_base + id; - } - } else { - // Search in runtime functions - id = search_address(addr, _extrs_addr, _extrs_length); - if (id < 0) { - ResourceMark rm; - const int buflen = 1024; - char* func_name = NEW_RESOURCE_ARRAY(char, buflen); - int offset = 0; - if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) { - if (offset > 0) { - // Could be address of C string - uint dist = (uint)pointer_delta(addr, (address)os::init, 1); - log_debug(aot, codecache)("Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in AOT Code Cache addresses table", - p2i(addr), dist, (const char*)addr); - assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance"); - return dist; - } + // Search in runtime functions + id = search_address(addr, _extrs_addr, _extrs_length); + if (id < 0) { + ResourceMark rm; + const int buflen = 1024; + char* func_name = NEW_RESOURCE_ARRAY(char, buflen); + int offset = 0; + if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) { + if (offset > 0) { + // Could be address of C string + uint dist = (uint)pointer_delta(addr, (address)os::init, 1); + log_debug(aot, codecache)("Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in AOT Code Cache addresses table", + p2i(addr), dist, (const char*)addr); + assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance"); + return dist; + } #ifdef ASSERT - reloc.print_current_on(tty); - code_blob->print_on(tty); - code_blob->print_code_on(tty); - assert(false, "Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset); + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + assert(false, "Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset); #endif - } else { + } else { #ifdef ASSERT - reloc.print_current_on(tty); - code_blob->print_on(tty); - code_blob->print_code_on(tty); - os::find(addr, tty); - assert(false, "Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr); + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + os::find(addr, tty); + assert(false, "Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr); #endif - } - } else { - return _extrs_base + id; } + } else { + return _extrs_base + id; } } return id; @@ -2113,3 +2496,161 @@ void AOTCodeCache::print_on(outputStream* st) { } } } + +// methods for managing entries in multi-stub blobs + + +AOTStubData::AOTStubData(BlobId blob_id) : + _blob_id(blob_id), + _cached_blob(nullptr), + _stub_cnt(0), + _ranges(nullptr), + _flags(0) { + assert(StubInfo::is_stubgen(blob_id), + "AOTStubData expects a multi-stub blob not %s", + StubInfo::name(blob_id)); + + // we cannot save or restore preuniversestubs because the cache + // cannot be accessed before initialising the universe + if (blob_id == BlobId::stubgen_preuniverse_id) { + // invalidate any attempt to use this + _flags |= INVALID; + return; + } + if (AOTCodeCache::is_on()) { + // allow update of stub entry addresses + if (AOTCodeCache::is_using_stub()) { + // allow stub loading + _flags |= USING; + } + if (AOTCodeCache::is_dumping_stub()) { + // allow stub saving + _flags |= DUMPING; + } + // we need to track all the blob's entries + _stub_cnt = StubInfo::stub_count(_blob_id); + _ranges = NEW_C_HEAP_ARRAY(StubAddrRange, _stub_cnt, mtCode); + for (int i = 0; i < _stub_cnt; i++) { + _ranges[i].default_init(); + } + } +} + +bool AOTStubData::load_code_blob() { + assert(is_using(), "should not call"); + assert(!is_invalid() && _cached_blob == nullptr, "repeated init"); + _cached_blob = AOTCodeCache::load_code_blob(AOTCodeEntry::StubGenBlob, + _blob_id, + this); + if (_cached_blob == nullptr) { + set_invalid(); + return false; + } else { + return true; + } +} + +bool AOTStubData::store_code_blob(CodeBlob& new_blob, CodeBuffer *code_buffer) { + assert(is_dumping(), "should not call"); + assert(_cached_blob == nullptr, "should not be loading and storing!"); + if (!AOTCodeCache::store_code_blob(new_blob, + AOTCodeEntry::StubGenBlob, + _blob_id, this, code_buffer)) { + set_invalid(); + return false; + } else { + return true; + } +} + +address AOTStubData::load_archive_data(StubId stub_id, address& end, GrowableArray
* entries, GrowableArray
* extras) { + assert(StubInfo::blob(stub_id) == _blob_id, "sanity check"); + if (is_invalid()) { + return nullptr; + } + int idx = StubInfo::stubgen_offset_in_blob(_blob_id, stub_id); + assert(idx >= 0 && idx < _stub_cnt, "invalid index %d for stub count %d", idx, _stub_cnt); + // ensure we have a valid associated range + StubAddrRange &range = _ranges[idx]; + int base = range.start_index(); + if (base < 0) { +#ifdef DEBUG + // reset index so we can idenitfy which ones we failed to find + range.init_entry(-2, 0); +#endif + return nullptr; + } + int count = range.count(); + assert(base >= 0, "sanity"); + assert(count >= 2, "sanity"); + // first two saved addresses are start and end + address start = _address_array.at(base); + end = _address_array.at(base + 1); + assert(start != nullptr, "failed to load start address of stub %s", StubInfo::name(stub_id)); + assert(end != nullptr, "failed to load end address of stub %s", StubInfo::name(stub_id)); + assert(start < end, "start address %p should be less than end %p address for stub %s", start, end, StubInfo::name(stub_id)); + + int entry_count = StubInfo::entry_count(stub_id); + // the address count must at least include the stub start, end + // and secondary addresses + assert(count >= entry_count + 1, "stub %s requires %d saved addresses but only has %d", StubInfo::name(stub_id), entry_count + 1, count); + + // caller must retrieve secondary entries if and only if they exist + assert((entry_count == 1) == (entries == nullptr), "trying to retrieve wrong number of entries for stub %s", StubInfo::name(stub_id)); + int index = 2; + if (entries != nullptr) { + assert(entries->length() == 0, "non-empty array when retrieving entries for stub %s!", StubInfo::name(stub_id)); + while (index < entry_count + 1) { + address entry = _address_array.at(base + index++); + assert(entry == nullptr || (start < entry && entry < end), "entry address %p not in range (%p, %p) for stub %s", entry, start, end, StubInfo::name(stub_id)); + entries->append(entry); + } + } + // caller must retrieve extras if and only if they exist + assert((index < count) == (extras != nullptr), "trying to retrieve wrong number of extras for stub %s", StubInfo::name(stub_id)); + if (extras != nullptr) { + assert(extras->length() == 0, "non-empty array when retrieving extras for stub %s!", StubInfo::name(stub_id)); + while (index < count) { + address extra = _address_array.at(base + index++); + assert(extra == nullptr || (start <= extra && extra <= end), "extra address %p not in range (%p, %p) for stub %s", extra, start, end, StubInfo::name(stub_id)); + extras->append(extra); + } + } + + return start; +} + +void AOTStubData::store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries, GrowableArray
* extras) { + assert(StubInfo::blob(stub_id) == _blob_id, "sanity check"); + assert(start != nullptr, "start address cannot be null"); + assert(end != nullptr, "end address cannot be null"); + assert(start < end, "start address %p should be less than end %p address for stub %s", start, end, StubInfo::name(stub_id)); + int idx = StubInfo::stubgen_offset_in_blob(_blob_id, stub_id); + StubAddrRange& range = _ranges[idx]; + assert(range.start_index() == -1, "sanity"); + int base = _address_array.length(); + assert(base >= 0, "sanity"); + // first two saved addresses are start and end + _address_array.append(start); + _address_array.append(end); + // caller must save secondary entries if and only if they exist + assert((StubInfo::entry_count(stub_id) == 1) == (entries == nullptr), "trying to save wrong number of entries for stub %s", StubInfo::name(stub_id)); + if (entries != nullptr) { + assert(entries->length() == StubInfo::entry_count(stub_id) - 1, "incorrect entry count %d when saving entries for stub %s!", entries->length(), StubInfo::name(stub_id)); + for (int i = 0; i < entries->length(); i++) { + address entry = entries->at(i); + assert(entry == nullptr || (start < entry && entry < end), "entry address %p not in range (%p, %p) for stub %s", entry, start, end, StubInfo::name(stub_id)); + _address_array.append(entry); + } + } + // caller may wish to save extra addresses + if (extras != nullptr) { + for (int i = 0; i < extras->length(); i++) { + address extra = extras->at(i); + // handler range end may be end -- it gets restored as nullptr + assert(extra == nullptr || (start <= extra && extra <= end), "extra address %p not in range (%p, %p) for stub %s", extra, start, end, StubInfo::name(stub_id)); + _address_array.append(extra); + } + } + range.init_entry(base, _address_array.length() - base); +} diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 52b0adfba48..c4ebe271767 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -25,8 +25,10 @@ #ifndef SHARE_CODE_AOTCODECACHE_HPP #define SHARE_CODE_AOTCODECACHE_HPP +#include "gc/shared/collectedHeap.hpp" #include "gc/shared/gc_globals.hpp" #include "runtime/stubInfo.hpp" +#include "utilities/hashTable.hpp" /* * AOT Code Cache collects code from Code Cache and corresponding metadata @@ -38,6 +40,7 @@ class CodeBuffer; class RelocIterator; class AOTCodeCache; +class AOTCodeReader; class AdapterBlob; class ExceptionBlob; class ImmutableOopMapSet; @@ -53,6 +56,7 @@ enum CompLevel : signed char; Fn(SharedBlob) \ Fn(C1Blob) \ Fn(C2Blob) \ + Fn(StubGenBlob) \ // Descriptor of AOT Code Cache's entry class AOTCodeEntry { @@ -114,48 +118,57 @@ class AOTCodeEntry { address dumptime_content_start_addr() const { return _dumptime_content_start_addr; } static bool is_valid_entry_kind(Kind kind) { return kind > None && kind < Kind_count; } - static bool is_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob; } + static bool is_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob || kind == StubGenBlob; } + static bool is_single_stub_blob(Kind kind) { return kind == SharedBlob || kind == C1Blob || kind == C2Blob; } + static bool is_multi_stub_blob(Kind kind) { return kind == StubGenBlob; } static bool is_adapter(Kind kind) { return kind == Adapter; } }; +// we use a hash table to speed up translation of external addresses +// or stub addresses to their corresponding indexes when dumping stubs +// or nmethods to the AOT code cache. +class AOTCodeAddressHashTable : public HashTable< + address, + int, + 36137, // prime number + AnyObj::C_HEAP, + mtCode> {}; + // Addresses of stubs, blobs and runtime finctions called from compiled code. class AOTCodeAddressTable : public CHeapObj { private: address* _extrs_addr; address* _stubs_addr; - address* _shared_blobs_addr; - address* _C1_blobs_addr; uint _extrs_length; - uint _stubs_length; - uint _shared_blobs_length; - uint _C1_blobs_length; bool _extrs_complete; - bool _early_stubs_complete; - bool _shared_blobs_complete; - bool _early_c1_complete; - bool _complete; + bool _shared_stubs_complete; + bool _c1_stubs_complete; + bool _c2_stubs_complete; + bool _stubgen_stubs_complete; + AOTCodeAddressHashTable* _hash_table; + void hash_address(address addr, int idx); public: AOTCodeAddressTable() : _extrs_addr(nullptr), _stubs_addr(nullptr), - _shared_blobs_addr(nullptr), - _C1_blobs_addr(nullptr), _extrs_length(0), - _stubs_length(0), - _shared_blobs_length(0), - _C1_blobs_length(0), _extrs_complete(false), - _early_stubs_complete(false), - _shared_blobs_complete(false), - _early_c1_complete(false), - _complete(false) + _shared_stubs_complete(false), + _c1_stubs_complete(false), + _c2_stubs_complete(false), + _stubgen_stubs_complete(false), + _hash_table(nullptr) { } void init_extrs(); - void init_early_stubs(); - void init_shared_blobs(); - void init_early_c1(); + void init_extrs2(); + void add_stub_entry(EntryId entry_id, address entry); + void add_external_addresses(GrowableArray
& addresses) NOT_CDS_RETURN; + void set_shared_stubs_complete(); + void set_c1_stubs_complete(); + void set_c2_stubs_complete(); + void set_stubgen_stubs_complete(); const char* add_C_string(const char* str); int id_for_C_string(address str); address address_for_C_string(int idx); @@ -163,120 +176,214 @@ class AOTCodeAddressTable : public CHeapObj { address address_for_id(int id); }; +// Auxiliary class used by AOTStubData to locate addresses owned by a +// stub in the _address_array. + +class StubAddrRange { +private: + // Index of the first address owned by a stub or -1 if none present + int _start_index; + // Total number of addresses owned by a stub, including in order: + // start address for stub code and first entry, (exclusive) end + // address for stub code, all secondary entry addresses, any + // auxiliary addresses + uint _naddr; + public: + StubAddrRange() : _start_index(-1), _naddr(0) {} + int start_index() { return _start_index; } + int count() { return _naddr; } + + void default_init() { + _start_index = -1; + _naddr = 0; + } + + void init_entry(int start_index, int naddr) { + _start_index = start_index; + _naddr = naddr; + } +}; + +// class used to save and restore details of stubs embedded in a +// multi-stub (StubGen) blob + +class AOTStubData : public StackObj { + friend class AOTCodeCache; + friend class AOTCodeReader; +private: + BlobId _blob_id; // must be a stubgen blob id + // whatever buffer blob was successfully loaded from the AOT cache + // following a call to load_code_blob or nullptr + CodeBlob *_cached_blob; + // Array of addresses owned by stubs. Each stub appends addresses to + // this array as a block, whether at the end of generation or at the + // end of restoration from the cache. The first two addresses in + // each block are the "start" and "end2 address of the stub. Any + // other visible addresses located within the range [start,end) + // follow, either extra entries, data addresses or SEGV-protected + // subrange start, end and handler addresses. In the special case + // that the SEGV handler address is the (external) common address + // handler the array will hold value nullptr. + GrowableArray
_address_array; + // count of how many stubs exist in the current blob (not all of + // which may actually be generated) + int _stub_cnt; + // array identifying range of entries in _address_array for each stub + // indexed by offset of stub in blob + StubAddrRange* _ranges; + + // flags indicating whether the AOT code cache is open and, if so, + // whether we are loading or storing stubs or have encountered any + // invalid stubs. + enum Flags { + USING = 1 << 0, // open and loading stubs + DUMPING = 1 << 1, // open and storing stubs + INVALID = 1 << 2, // found invalid stub when loading + }; + + uint32_t _flags; + + void set_invalid() { _flags |= INVALID; } + + StubAddrRange& get_range(int idx) const { return _ranges[idx]; } + GrowableArray
& address_array() { return _address_array; } + // accessor for entry/auxiliary addresses defaults to start entry +public: + AOTStubData(BlobId blob_id) NOT_CDS({}); + + ~AOTStubData() CDS_ONLY({FREE_C_HEAP_ARRAY(StubAddrRange, _ranges);}) NOT_CDS({}) + + bool is_using() CDS_ONLY({ return (_flags & USING) != 0; }) NOT_CDS_RETURN_(false); + bool is_dumping() CDS_ONLY({ return (_flags & DUMPING) != 0; }) NOT_CDS_RETURN_(false); + bool is_invalid() CDS_ONLY({ return (_flags & INVALID) != 0; }) NOT_CDS_RETURN_(false); + + BlobId blob_id() { return _blob_id; } + bool load_code_blob() NOT_CDS_RETURN_(true); + bool store_code_blob(CodeBlob& new_blob, CodeBuffer *code_buffer) NOT_CDS_RETURN_(true); + + address load_archive_data(StubId stub_id, address &end, GrowableArray
* entries = nullptr, GrowableArray
* extras = nullptr) NOT_CDS_RETURN_(nullptr); + void store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries = nullptr, GrowableArray
* extras = nullptr) NOT_CDS_RETURN; + + const AOTStubData* as_const() { return (const AOTStubData*)this; } +}; + +#define AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \ + do_var(int, AllocateInstancePrefetchLines) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchDistance) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchLines) /* stubs and nmethods */ \ + do_var(int, AllocatePrefetchStepSize) /* stubs and nmethods */ \ + do_var(uint, CodeEntryAlignment) /* array copy stubs and nmethods */ \ + do_var(bool, UseCompressedOops) /* stubs and nmethods */ \ + do_var(bool, EnableContended) /* nmethods */ \ + do_var(intx, OptoLoopAlignment) /* array copy stubs and nmethods */ \ + do_var(bool, RestrictContended) /* nmethods */ \ + do_var(bool, UseAESCTRIntrinsics) \ + do_var(bool, UseAESIntrinsics) \ + do_var(bool, UseBASE64Intrinsics) \ + do_var(bool, UseChaCha20Intrinsics) \ + do_var(bool, UseCRC32CIntrinsics) \ + do_var(bool, UseCRC32Intrinsics) \ + do_var(bool, UseDilithiumIntrinsics) \ + do_var(bool, UseGHASHIntrinsics) \ + do_var(bool, UseKyberIntrinsics) \ + do_var(bool, UseMD5Intrinsics) \ + do_var(bool, UsePoly1305Intrinsics) \ + do_var(bool, UseSecondarySupersTable) \ + do_var(bool, UseSHA1Intrinsics) \ + do_var(bool, UseSHA256Intrinsics) \ + do_var(bool, UseSHA3Intrinsics) \ + do_var(bool, UseSHA512Intrinsics) \ + do_var(bool, UseVectorizedMismatchIntrinsic) \ + do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \ + do_fun(int, CompressedOops_shift, CompressedOops::shift()) \ + do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \ + do_fun(bool, JavaAssertions_userClassDefault, JavaAssertions::userClassDefault()) \ + do_fun(CollectedHeap::Name, Universe_heap_kind, Universe::heap()->kind()) \ + // END + +#ifdef COMPILER2 +#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \ + do_var(intx, ArrayOperationPartialInlineSize) /* array copy stubs and nmethods */ \ + do_var(intx, MaxVectorSize) /* array copy/fill stubs */ \ + do_var(bool, UseMontgomeryMultiplyIntrinsic) \ + do_var(bool, UseMontgomerySquareIntrinsic) \ + do_var(bool, UseMulAddIntrinsic) \ + do_var(bool, UseMultiplyToLenIntrinsic) \ + do_var(bool, UseSquareToLenIntrinsic) \ + // END +#else +#define AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) +#endif + +#if INCLUDE_JVMCI +#define AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) \ + do_var(bool, EnableJVMCI) /* adapters and nmethods */ \ + // END +#else +#define AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) +#endif + +#if defined(AARCH64) && !defined(ZERO) +#define AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \ + do_var(intx, BlockZeroingLowLimit) /* array fill stubs */ \ + do_var(intx, PrefetchCopyIntervalInBytes) /* array copy stubs */ \ + do_var(int, SoftwarePrefetchHintDistance) /* array fill stubs */ \ + do_var(bool, UseBlockZeroing) \ + do_var(bool, UseLSE) /* stubs and nmethods */ \ + do_var(uint, UseSVE) /* stubs and nmethods */ \ + do_var(bool, UseSecondarySupersCache) \ + do_var(bool, UseSIMDForArrayEquals) /* array copy stubs and nmethods */ \ + do_var(bool, UseSIMDForBigIntegerShiftIntrinsics) \ + do_var(bool, UseSIMDForMemoryOps) /* array copy stubs and nmethods */ \ + do_var(bool, UseSIMDForSHA3Intrinsic) /* SHA3 stubs */ \ + do_var(bool, UseSimpleArrayEquals) \ + // END +#else +#define AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) +#endif + +#if defined(X86) && !defined(ZERO) +#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \ + do_var(int, AVX3Threshold) /* array copy stubs and nmethods */ \ + do_var(bool, EnableX86ECoreOpts) /* nmethods */ \ + do_var(int, UseAVX) /* array copy stubs and nmethods */ \ + do_var(bool, UseAPX) /* nmethods and stubs */ \ + do_var(bool, UseLibmIntrinsic) \ + do_var(bool, UseIntPolyIntrinsics) \ + // END +#else +#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) +#endif + +#define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_JVMCI_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \ + AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \ + // END + +#define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name; +#define AOTCODECACHE_DECLARE_FUN(type, name, func) type _saved_ ## name; + class AOTCodeCache : public CHeapObj { // Classes used to describe AOT code cache. protected: class Config { + AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_DECLARE_VAR, AOTCODECACHE_DECLARE_FUN) + + // Special configs that cannot be checked with macros address _compressedOopBase; - uint _compressedOopShift; - uint _compressedKlassShift; - uint _contendedPaddingWidth; - uint _gc; - uint _optoLoopAlignment; - uint _codeEntryAlignment; - uint _allocatePrefetchLines; - uint _allocateInstancePrefetchLines; - uint _allocatePrefetchDistance; - uint _allocatePrefetchStepSize; -#ifdef COMPILER2 - uint _maxVectorSize; - uint _arrayOperationPartialInlineSize; -#endif // COMPILER2 - enum Flags { - none = 0, - debugVM = 1, - compressedOops = 2, - useTLAB = 4, - systemClassAssertions = 8, - userClassAssertions = 16, - enableContendedPadding = 32, - restrictContendedPadding = 64 - }; - uint _flags; - enum IntrinsicsUseFlags { - use_none = 0, - useCRC32 = 1 << 0, - useCRC32C = 1 << 1, - useMultiplyToLen = 1 << 2, - useSquareToLen = 1 << 3, - useMulAdd = 1 << 4, - useMontgomeryMultiply = 1 << 5, - useMontgomerySquare = 1 << 6, - useChaCha20 = 1 << 7, - useDilithium = 1 << 8, - useKyber = 1 << 9, - useBASE64 = 1 << 10, - useAdler32 = 1 << 11, - useAES = 1 << 12, - useAESCTR = 1 << 13, - useGHASH = 1 << 14, - useMD5 = 1 << 15, - useSHA1 = 1 << 16, - useSHA256 = 1 << 17, - useSHA512 = 1 << 18, - useSHA3 = 1 << 19, - usePoly1305 = 1 << 20, - useVectorizedMismatch = 1 << 21, - useSecondarySupersTable = 1 << 22, - }; - uint _use_intrinsics_flags; - bool test_flag(enum Flags flag) const { return (_flags & flag) != 0; } - bool test_use_flag(enum IntrinsicsUseFlags flag) const { return (_use_intrinsics_flags & flag) != 0; } - void set_flag(enum Flags flag) { _flags |= flag; } - void set_use_flag(enum IntrinsicsUseFlags flag) { _use_intrinsics_flags |= flag; } + #if defined(X86) && !defined(ZERO) - uint _avx3threshold; - uint _useAVX; - enum X86Flags { - x86_none = 0, - x86_enableX86ECoreOpts = 1, - x86_useUnalignedLoadStores = 2, - x86_useAPX = 4 - }; - uint _x86_flags; - enum X86IntrinsicsUseFlags { - x86_use_none = 0, - x86_useLibm = 1 << 1, - x86_useIntPoly = 1 << 2, - }; - uint _x86_use_intrinsics_flags; - bool test_x86_flag(enum X86Flags flag) const { return (_x86_flags & flag) != 0; } - bool test_x86_use_flag(enum X86IntrinsicsUseFlags flag) const { return (_x86_use_intrinsics_flags & flag) != 0; } - void set_x86_flag(enum X86Flags flag) { _x86_flags |= flag; } - void set_x86_use_flag(enum X86IntrinsicsUseFlags flag) { _x86_use_intrinsics_flags |= flag; } -#endif // defined(X86) && !defined(ZERO) + bool _useUnalignedLoadStores; +#endif + #if defined(AARCH64) && !defined(ZERO) - // this is global but x86 does not use it and aarch64 does - uint _prefetchCopyIntervalInBytes; - uint _blockZeroingLowLimit; - uint _softwarePrefetchHintDistance; - uint _useSVE; - enum AArch64Flags { - aarch64_none = 0, - aarch64_avoidUnalignedAccesses = 1, - aarch64_useSIMDForMemoryOps = 2, - aarch64_useSIMDForArrayEquals = 4, - aarch64_useSIMDForSHA3 = 8, - aarch64_useLSE = 16, - }; - uint _aarch64_flags; - enum AArch64IntrinsicsUseFlags { - aarch64_use_none = 0, - aarch64_useBlockZeroing = 1 << 0, - aarch64_useSIMDForBigIntegerShift = 1 << 1, - aarch64_useSimpleArrayEquals = 1 << 2, - aarch64_useSecondarySupersCache = 1 << 3, - }; - uint _aarch64_use_intrinsics_flags; - bool test_aarch64_flag(enum AArch64Flags flag) const { return (_aarch64_flags & flag) != 0; } - bool test_aarch64_use_flag(enum AArch64IntrinsicsUseFlags flag) const { return (_aarch64_use_intrinsics_flags & flag) != 0; } - void set_aarch64_flag(enum AArch64Flags flag) { _aarch64_flags |= flag; } - void set_aarch64_use_flag(enum AArch64IntrinsicsUseFlags flag) { _aarch64_use_intrinsics_flags |= flag; } -#endif // defined(AARCH64) && !defined(ZERO) -#if INCLUDE_JVMCI - uint _enableJVMCI; -#endif // INCLUDE_JVMCI + bool _avoidUnalignedAccesses; +#endif + uint _cpu_features_offset; // offset in the cache where cpu features are stored public: void record(uint cpu_features_offset); @@ -297,17 +404,18 @@ class AOTCodeCache : public CHeapObj { uint _entries_offset; // offset of AOTCodeEntry array describing entries uint _adapters_count; uint _shared_blobs_count; + uint _stubgen_blobs_count; uint _C1_blobs_count; uint _C2_blobs_count; Config _config; // must be the last element as there is trailing data stored immediately after Config public: void init(uint cache_size, - uint strings_count, uint strings_offset, - uint entries_count, uint entries_offset, - uint adapters_count, uint shared_blobs_count, - uint C1_blobs_count, uint C2_blobs_count, - uint cpu_features_offset) { + uint strings_count, uint strings_offset, + uint entries_count, uint entries_offset, + uint adapters_count, uint shared_blobs_count, + uint stubgen_blobs_count, uint C1_blobs_count, + uint C2_blobs_count, uint cpu_features_offset) { _version = AOT_CODE_VERSION; _cache_size = cache_size; _strings_count = strings_count; @@ -316,6 +424,7 @@ class AOTCodeCache : public CHeapObj { _entries_offset = entries_offset; _adapters_count = adapters_count; _shared_blobs_count = shared_blobs_count; + _stubgen_blobs_count = stubgen_blobs_count; _C1_blobs_count = C1_blobs_count; _C2_blobs_count = C2_blobs_count; _config.record(cpu_features_offset); @@ -328,6 +437,7 @@ class AOTCodeCache : public CHeapObj { uint entries_count() const { return _entries_count; } uint entries_offset() const { return _entries_offset; } uint adapters_count() const { return _adapters_count; } + uint stubgen_blobs_count() const { return _stubgen_blobs_count; } uint shared_blobs_count() const { return _shared_blobs_count; } uint C1_blobs_count() const { return _C1_blobs_count; } uint C2_blobs_count() const { return _C2_blobs_count; } @@ -377,6 +487,7 @@ class AOTCodeCache : public CHeapObj { void clear_lookup_failed() { _lookup_failed = false; } bool lookup_failed() const { return _lookup_failed; } + void add_stub_entry(EntryId entry_id, address entry) NOT_CDS_RETURN; public: AOTCodeCache(bool is_dumping, bool is_using); @@ -392,9 +503,12 @@ class AOTCodeCache : public CHeapObj { void load_strings(); int store_strings(); - static void init_early_stubs_table() NOT_CDS_RETURN; - static void init_shared_blobs_table() NOT_CDS_RETURN; - static void init_early_c1_table() NOT_CDS_RETURN; + static void set_shared_stubs_complete() NOT_CDS_RETURN; + static void set_c1_stubs_complete() NOT_CDS_RETURN ; + static void set_c2_stubs_complete() NOT_CDS_RETURN; + static void set_stubgen_stubs_complete() NOT_CDS_RETURN; + + void add_stub_entries(StubId stub_id, address start, GrowableArray
*entries = nullptr, int offset = -1) NOT_CDS_RETURN; address address_for_C_string(int idx) const { return _table->address_for_C_string(idx); } address address_for_id(int id) const { return _table->address_for_id(id); } @@ -414,22 +528,41 @@ class AOTCodeCache : public CHeapObj { bool finish_write(); - bool write_relocations(CodeBlob& code_blob); + bool write_relocations(CodeBlob& code_blob, RelocIterator& iter); bool write_oop_map_set(CodeBlob& cb); + bool write_stub_data(CodeBlob& blob, AOTStubData *stub_data); #ifndef PRODUCT bool write_asm_remarks(CodeBlob& cb); bool write_dbg_strings(CodeBlob& cb); #endif // PRODUCT +private: + // internal private API to save and restore blobs + static bool store_code_blob(CodeBlob& blob, + AOTCodeEntry::Kind entry_kind, + uint id, + const char* name, + AOTStubData* stub_data, + CodeBuffer* code_buffer) NOT_CDS_RETURN_(false); + + static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, + uint id, + const char* name, + AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr); + +public: // save and restore API for non-enumerable code blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, - uint id, const char* name) NOT_CDS_RETURN_(false); + uint id, + const char* name) NOT_CDS_RETURN_(false); static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, uint id, const char* name) NOT_CDS_RETURN_(nullptr); // save and restore API for enumerable code blobs + + // API for single-stub blobs static bool store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, BlobId id) NOT_CDS_RETURN_(false); @@ -437,6 +570,22 @@ class AOTCodeCache : public CHeapObj { static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, BlobId id) NOT_CDS_RETURN_(nullptr); + // API for multi-stub blobs -- for use by class StubGenerator. + + static bool store_code_blob(CodeBlob& blob, + AOTCodeEntry::Kind kind, + BlobId id, + AOTStubData* stub_data, + CodeBuffer *code_buffer) NOT_CDS_RETURN_(false); + + static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, + BlobId id, + AOTStubData* stub_data) NOT_CDS_RETURN_(nullptr); + + static void publish_external_addresses(GrowableArray
& addresses) NOT_CDS_RETURN; + // publish all entries for a code blob in code cache address table + static void publish_stub_addresses(CodeBlob &code_blob, BlobId id, AOTStubData *stub_data) NOT_CDS_RETURN; + static uint store_entries_cnt() { if (is_on_for_dump()) { return cache()->_store_entries_cnt; @@ -458,9 +607,14 @@ class AOTCodeCache : public CHeapObj { return true; } public: + // marker used where an address offset needs to be stored for later + // retrieval and the address turns out to be null + static const uint NULL_ADDRESS_MARKER = UINT_MAX; + static AOTCodeCache* cache() { assert(_passed_init2, "Too early to ask"); return _cache; } static void initialize() NOT_CDS_RETURN; static void init2() NOT_CDS_RETURN; + static void init3() NOT_CDS_RETURN; static void dump() NOT_CDS_RETURN; static bool is_on() CDS_ONLY({ return cache() != nullptr; }) NOT_CDS_RETURN_(false); static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false); @@ -481,7 +635,7 @@ class AOTCodeCache : public CHeapObj { // Concurent AOT code reader class AOTCodeReader { private: - const AOTCodeCache* _cache; + AOTCodeCache* _cache; const AOTCodeEntry* _entry; const char* _load_buffer; // Loaded cached code buffer uint _read_position; // Position in _load_buffer @@ -498,13 +652,18 @@ class AOTCodeReader { // They should be set before calling it. const char* _name; address _reloc_data; + int _reloc_count; ImmutableOopMapSet* _oop_maps; + AOTCodeEntry::Kind _entry_kind; + int _id; + AOTStubData* _stub_data; AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } ImmutableOopMapSet* read_oop_map_set(); + void read_stub_data(CodeBlob* code_blob, AOTStubData *stub_data); - void fix_relocations(CodeBlob* code_blob); + void fix_relocations(CodeBlob* code_blob, RelocIterator& iter); #ifndef PRODUCT void read_asm_remarks(AsmRemarks& asm_remarks); void read_dbg_strings(DbgStrings& dbg_strings); @@ -513,7 +672,7 @@ class AOTCodeReader { public: AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); - CodeBlob* compile_code_blob(const char* name); + CodeBlob* compile_code_blob(const char* name, AOTCodeEntry::Kind entry_kind, int id, AOTStubData* stub_data = nullptr); void restore(CodeBlob* code_blob); }; diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index a7d939e590d..e0c286937d0 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -326,6 +326,11 @@ RuntimeBlob::RuntimeBlob( : CodeBlob(name, kind, cb, size, header_size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments, align_up(cb->total_relocation_size(), oopSize)) { + if (code_size() == 0) { + // Nothing to copy + return; + } + cb->copy_code_and_locs_to(this); // Flush generated code diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 0e2aa208854..a302df418d7 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -2038,7 +2038,7 @@ void nmethod::copy_values(GrowableArray* array) { // The code and relocations have already been initialized by the // CodeBlob constructor, so it is valid even at this early point to // iterate over relocations and patch the code. - fix_oop_relocations(nullptr, nullptr, /*initialize_immediates=*/ true); + fix_oop_relocations(/*initialize_immediates=*/ true); } void nmethod::copy_values(GrowableArray* array) { @@ -2050,24 +2050,42 @@ void nmethod::copy_values(GrowableArray* array) { } } -void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) { +bool nmethod::fix_oop_relocations(bool initialize_immediates) { // re-patch all oop-bearing instructions, just in case some oops moved - RelocIterator iter(this, begin, end); + RelocIterator iter(this); + bool modified_code = false; while (iter.next()) { if (iter.type() == relocInfo::oop_type) { oop_Relocation* reloc = iter.oop_reloc(); - if (initialize_immediates && reloc->oop_is_immediate()) { + if (!reloc->oop_is_immediate()) { + // Refresh the oop-related bits of this instruction. + reloc->set_value(reloc->value()); + modified_code = true; + } else if (initialize_immediates) { oop* dest = reloc->oop_addr(); jobject obj = *reinterpret_cast(dest); initialize_immediate_oop(dest, obj); } - // Refresh the oop-related bits of this instruction. - reloc->fix_oop_relocation(); } else if (iter.type() == relocInfo::metadata_type) { metadata_Relocation* reloc = iter.metadata_reloc(); reloc->fix_metadata_relocation(); + modified_code |= !reloc->metadata_is_immediate(); } } + return modified_code; +} + +void nmethod::fix_oop_relocations() { + ICacheInvalidationContext icic; + fix_oop_relocations(&icic); +} + +void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) { + assert(icic != nullptr, "must provide context to track if code was modified"); + bool modified_code = fix_oop_relocations(/*initialize_immediates=*/ false); + if (modified_code) { + icic->set_has_modified_code(); + } } static void install_post_call_nop_displacement(nmethod* nm, address pc) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 092da181f12..ea8c0e2ad5d 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -41,6 +41,7 @@ class Dependencies; class DirectiveSet; class DebugInformationRecorder; class ExceptionHandlerTable; +class ICacheInvalidationContext; class ImplicitExceptionTable; class JvmtiThreadState; class MetadataClosure; @@ -801,15 +802,15 @@ class nmethod : public CodeBlob { // Relocation support private: - void fix_oop_relocations(address begin, address end, bool initialize_immediates); + bool fix_oop_relocations(bool initialize_immediates); inline void initialize_immediate_oop(oop* dest, jobject handle); protected: address oops_reloc_begin() const; public: - void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); } - void fix_oop_relocations() { fix_oop_relocations(nullptr, nullptr, false); } + void fix_oop_relocations(ICacheInvalidationContext* icic); + void fix_oop_relocations(); bool is_at_poll_return(address pc); bool is_at_poll_or_poll_return(address pc); diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 2a6335e2118..73e4b6de7b4 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -590,15 +590,15 @@ oop oop_Relocation::oop_value() { return *oop_addr(); } - void oop_Relocation::fix_oop_relocation() { + // TODO: we need to add some assert here that ICache::invalidate_range is called in the code + // which uses this function. if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: set_value(value()); } } - void oop_Relocation::verify_oop_relocation() { if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 84a0b7588ef..a72d5fc5cf9 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1131,23 +1131,34 @@ bool G1ConcurrentMark::scan_root_regions(WorkerThreads* workers, bool concurrent // // Concurrent gc threads enter an STS when starting the task, so they stop, then // continue after that safepoint. - bool do_scan = !root_regions()->work_completed() && !has_root_region_scan_aborted(); + // + // Must not use G1CMRootMemRegions::work_completed() here because we need to get a + // consistent view of the value containing the number of remaining regions across the + // usages below. The safepoint/gc may already be running and modifying it + // while this code is still executing. + uint num_remaining = root_regions()->num_remaining_regions(); + bool do_scan = num_remaining > 0 && !has_root_region_scan_aborted(); if (do_scan) { // Assign one worker to each root-region but subject to the max constraint. // The constraint is also important to avoid accesses beyond the allocated per-worker // marking helper data structures. We might get passed different WorkerThreads with // different number of threads (potential worker ids) than helper data structures when // completing this work during GC. - const uint num_workers = MIN2(root_regions()->num_remaining_regions(), + const uint num_workers = MIN2(num_remaining, _max_concurrent_workers); assert(num_workers > 0, "no more remaining root regions to process"); G1CMRootRegionScanTask task(this, concurrent); log_debug(gc, ergo)("Running %s using %u workers for %u work units.", - task.name(), num_workers, root_regions()->num_remaining_regions()); + task.name(), num_workers, num_remaining); workers->run_task(&task, num_workers); } + // At the end of this method, we can re-read num_remaining() in the assert: either + // we got non-zero above and we processed all root regions (and it must be zero + // after the worker task synchronization) or it had already been zero. We also + // can't have started another concurrent cycle that could have set it to something else + // while still in the concurrent cycle (if called concurrently). assert_root_region_scan_completed_or_aborted(); return do_scan; @@ -2341,20 +2352,16 @@ void G1CMTask::drain_local_queue(bool partially) { } } -size_t G1CMTask::start_partial_array_processing(oop obj) { - assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size()); - - objArrayOop obj_array = objArrayOop(obj); - size_t array_length = obj_array->length(); - - size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj_array, nullptr, array_length); +size_t G1CMTask::start_partial_array_processing(objArrayOop obj) { + assert(obj->length() >= (int)ObjArrayMarkingStride, "Must be a large array object %d", obj->length()); // Mark objArray klass metadata - if (_cm_oop_closure->do_metadata()) { - _cm_oop_closure->do_klass(obj_array->klass()); - } + process_klass(obj->klass()); + + size_t array_length = obj->length(); + size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj, nullptr, array_length); - process_array_chunk(obj_array, 0, initial_chunk_size); + process_array_chunk(obj, 0, initial_chunk_size); // Include object header size return objArrayOopDesc::object_size(checked_cast(initial_chunk_size)); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 7aa93947204..f9287f673d2 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -844,12 +844,10 @@ class G1CMTask : public TerminatorTerminator { // mark bitmap scan, and so needs to be pushed onto the mark stack. bool is_below_finger(oop obj, HeapWord* global_finger) const; - template void process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen); - static bool should_be_sliced(oop obj); // Start processing the given objArrayOop by first pushing its continuations and // then scanning the first chunk including the header. - size_t start_partial_array_processing(oop obj); + size_t start_partial_array_processing(objArrayOop obj); // Process the given continuation. Returns the number of words scanned. size_t process_partial_array(const G1TaskQueueEntry& task, bool stolen); // Apply the closure to the given range of elements in the objArray. @@ -918,6 +916,9 @@ class G1CMTask : public TerminatorTerminator { template inline bool deal_with_reference(T* p); + // Scan the klass and visit its children. + inline void process_klass(Klass* klass); + // Scans an object and visits its children. inline void process_entry(G1TaskQueueEntry task_entry, bool stolen); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 21167d5cae9..094f4dca994 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -106,9 +106,27 @@ inline void G1CMMarkStack::iterate(Fn fn) const { } #endif +inline void G1CMTask::process_klass(Klass* klass) { + _cm_oop_closure->do_klass(klass); +} + // It scans an object and visits its children. inline void G1CMTask::process_entry(G1TaskQueueEntry task_entry, bool stolen) { - process_grey_task_entry(task_entry, stolen); + assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), + "Any stolen object should be a slice or marked"); + + if (task_entry.is_partial_array_state()) { + _words_scanned += process_partial_array(task_entry, stolen); + } else { + oop obj = task_entry.to_oop(); + if (should_be_sliced(obj)) { + _words_scanned += start_partial_array_processing(objArrayOop(obj)); + } else { + _words_scanned += obj->oop_iterate_size(_cm_oop_closure); + } + } + + check_limits(); } inline void G1CMTask::push(G1TaskQueueEntry task_entry) { @@ -160,27 +178,6 @@ inline bool G1CMTask::is_below_finger(oop obj, HeapWord* global_finger) const { return objAddr < global_finger; } -template -inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen) { - assert(scan || (!task_entry.is_partial_array_state() && task_entry.to_oop()->is_typeArray()), "Skipping scan of grey non-typeArray"); - assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), - "Any stolen object should be a slice or marked"); - - if (scan) { - if (task_entry.is_partial_array_state()) { - _words_scanned += process_partial_array(task_entry, stolen); - } else { - oop obj = task_entry.to_oop(); - if (should_be_sliced(obj)) { - _words_scanned += start_partial_array_processing(obj); - } else { - _words_scanned += obj->oop_iterate_size(_cm_oop_closure); - } - } - } - check_limits(); -} - inline bool G1CMTask::should_be_sliced(oop obj) { return obj->is_objArray() && ((objArrayOop)obj)->length() >= (int)ObjArrayMarkingStride; } @@ -272,7 +269,6 @@ inline bool G1CMTask::make_reference_grey(oop obj) { // be pushed on the stack. So, some duplicate work, but no // correctness problems. if (is_below_finger(obj, global_finger)) { - G1TaskQueueEntry entry(obj); if (obj->is_typeArray()) { // Immediately process arrays of primitive types, rather // than pushing on the mark stack. This keeps us from @@ -284,8 +280,8 @@ inline bool G1CMTask::make_reference_grey(oop obj) { // by only doing a bookkeeping update and avoiding the // actual scan of the object - a typeArray contains no // references, and the metadata is built-in. - process_grey_task_entry(entry, false /* stolen */); } else { + G1TaskQueueEntry entry(obj); push(entry); } } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index d5622ed5d79..45ba2740ea5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -84,7 +84,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // Choose the collection set filter_regions(collection_set); - if (!collection_set->is_empty() && _generation->is_global()) { + if (_generation->is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, @@ -94,7 +94,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // coalesce those regions. Only the old regions which are not part of the collection set at this point are // eligible for coalescing. As implemented now, this has the side effect of possibly initiating mixed-evacuations // after a global cycle for old regions that were not included in this collection set. - heap->old_generation()->prepare_for_mixed_collections_after_global_gc(); + heap->old_generation()->transition_old_generation_after_global_gc(); } } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 33c91e27be5..0789fd5cb1c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -576,7 +576,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { } else if (has_coalesce_and_fill_candidates()) { _old_generation->transition_to(ShenandoahOldGeneration::FILLING); } else { - _old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + _old_generation->transition_to(ShenandoahOldGeneration::IDLE); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 8cd8a390c4a..1873d818093 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -55,7 +55,7 @@ bool ShenandoahDegenGC::collect(GCCause::Cause cause) { vmop_degenerated(); ShenandoahHeap* heap = ShenandoahHeap::heap(); if (heap->mode()->is_generational()) { - bool is_bootstrap_gc = heap->old_generation()->is_bootstrapping(); + bool is_bootstrap_gc = heap->young_generation()->is_bootstrap_cycle(); heap->mmu_tracker()->record_degenerated(GCId::current(), is_bootstrap_gc); const char* msg = is_bootstrap_gc? "At end of Degenerated Bootstrap Old GC": "At end of Degenerated Young GC"; heap->log_heap_status(msg); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 064f43ffd6b..aa6a4a9bab2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -422,12 +422,11 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She } // Coalescing threads completed and nothing was cancelled. it is safe to transition from this state. - old_generation->transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + old_generation->transition_to(ShenandoahOldGeneration::IDLE); return; } - case ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP: - old_generation->transition_to(ShenandoahOldGeneration::BOOTSTRAPPING); - case ShenandoahOldGeneration::BOOTSTRAPPING: { + case ShenandoahOldGeneration::IDLE: + old_generation->transition_to(ShenandoahOldGeneration::MARKING); // Configure the young generation's concurrent mark to put objects in // old regions into the concurrent mark queues associated with the old // generation. The young cycle will run as normal except that rather than @@ -450,8 +449,6 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She // and init mark for the concurrent mark. All of that work will have been // done by the bootstrapping young cycle. set_gc_mode(servicing_old); - old_generation->transition_to(ShenandoahOldGeneration::MARKING); - } case ShenandoahOldGeneration::MARKING: { ShenandoahGCSession session(request.cause, old_generation); bool marking_complete = resume_concurrent_old_cycle(old_generation, request.cause); @@ -644,12 +641,6 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh if (request.generation->is_global()) { assert(_heap->old_generation()->task_queues()->is_empty(), "Unexpected old generation marking tasks"); assert(_heap->global_generation()->task_queues()->is_empty(), "Unexpected global generation marking tasks"); - } else { - assert(request.generation->is_young(), "Expected degenerated young cycle, if not global."); - ShenandoahOldGeneration* old = _heap->old_generation(); - if (old->is_bootstrapping()) { - old->transition_to(ShenandoahOldGeneration::MARKING); - } } } @@ -681,7 +672,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera // Cancel the old GC and wait for the control thread to start servicing the new request. log_info(gc)("Preempting old generation mark to allow %s GC", generation->name()); while (gc_mode() == servicing_old) { - ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc); + _heap->cancel_gc(GCCause::_shenandoah_concurrent_gc); notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation); ml.wait(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 16a24da8b1c..4ad7d2a1ae5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -114,7 +114,7 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _promotable_regular_regions(0), _is_parsable(true), _card_scan(nullptr), - _state(WAITING_FOR_BOOTSTRAP), + _state(IDLE), _growth_percent_before_collection(INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION) { assert(type() == ShenandoahGenerationType::OLD, "OO sanity"); @@ -339,7 +339,7 @@ void ShenandoahOldGeneration::cancel_gc() { shenandoah_assert_safepoint(); if (is_idle()) { #ifdef ASSERT - validate_waiting_for_bootstrap(); + validate_idle(); #endif } else { log_info(gc)("Terminating old gc cycle."); @@ -350,7 +350,7 @@ void ShenandoahOldGeneration::cancel_gc() { // Remove old generation access to young generation mark queues ShenandoahHeap::heap()->young_generation()->set_old_gen_task_queues(nullptr); // Transition to IDLE now. - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); } } @@ -477,9 +477,8 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent const char* ShenandoahOldGeneration::state_name(State state) { switch (state) { - case WAITING_FOR_BOOTSTRAP: return "Waiting for Bootstrap"; + case IDLE: return "Idle"; case FILLING: return "Coalescing"; - case BOOTSTRAPPING: return "Bootstrapping"; case MARKING: return "Marking"; case EVACUATING: return "Evacuating"; case EVACUATING_AFTER_GLOBAL: return "Evacuating (G)"; @@ -517,7 +516,7 @@ void ShenandoahOldGeneration::transition_to(State new_state) { // the old generation in the respective states (EVACUATING or FILLING). After a Full GC, // the mark bitmaps are all reset, all regions are parsable and the mark context will // not be "complete". After a Full GC, remembered set scans will _not_ use the mark bitmap -// and we expect the old generation to be waiting for bootstrap. +// and we expect the old generation to be idle. // // +-----------------+ // +------------> | FILLING | <---+ @@ -526,19 +525,12 @@ void ShenandoahOldGeneration::transition_to(State new_state) { // | | | | // | | | Filling Complete | <-> A global collection may // | | v | move the old generation -// | | +-----------------+ | directly from waiting for -// +-- |-- |--------> | WAITING | | bootstrap to filling or -// | | | +---- | FOR BOOTSTRAP | ----+ evacuating. It may also -// | | | | +-----------------+ move from filling to waiting -// | | | | | for bootstrap. -// | | | | | Reset Bitmap -// | | | | v -// | | | | +-----------------+ +----------------------+ -// | | | | | BOOTSTRAP | <-> | YOUNG GC | -// | | | | | | | (RSet Parses Region) | -// | | | | +-----------------+ +----------------------+ +// | | +-----------------+ | directly from idle to +// +-- |-- |--------> | IDLE | | filling or evacuating. +// | | | +---- | | ----+ It may also move from +// | | | | +-----------------+ filling to idle. // | | | | | -// | | | | | Old Marking +// | | | | | Reset Bitmap + Start Marking // | | | | v // | | | | +-----------------+ +----------------------+ // | | | | | MARKING | <-> | YOUNG GC | @@ -564,29 +556,23 @@ void ShenandoahOldGeneration::validate_transition(State new_state) { ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); switch (new_state) { case FILLING: - assert(_state != BOOTSTRAPPING, "Cannot begin making old regions parsable after bootstrapping"); assert(is_mark_complete(), "Cannot begin filling without first completing marking, state is '%s'", state_name(_state)); assert(_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot begin filling without something to fill."); break; - case WAITING_FOR_BOOTSTRAP: + case IDLE: // GC cancellation can send us back here from any state. - validate_waiting_for_bootstrap(); - break; - case BOOTSTRAPPING: - assert(_state == WAITING_FOR_BOOTSTRAP, "Cannot reset bitmap without making old regions parsable, state is '%s'", state_name(_state)); - assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot bootstrap with mixed collection candidates"); - assert(!heap->is_prepare_for_old_mark_in_progress(), "Cannot still be making old regions parsable."); + validate_idle(); break; case MARKING: - assert(_state == BOOTSTRAPPING, "Must have finished bootstrapping before marking, state is '%s'", state_name(_state)); - assert(heap->young_generation()->old_gen_task_queues() != nullptr, "Young generation needs old mark queues."); - assert(heap->is_concurrent_old_mark_in_progress(), "Should be marking old now."); + assert(_state == IDLE, "Must be idle before marking, state is '%s'", state_name(_state)); + assert(_old_heuristics->unprocessed_old_collection_candidates() == 0, "Cannot start marking with mixed collection candidates"); + assert(!heap->is_prepare_for_old_mark_in_progress(), "Cannot still be making old regions parsable."); break; case EVACUATING_AFTER_GLOBAL: assert(_state == EVACUATING, "Must have been evacuating, state is '%s'", state_name(_state)); break; case EVACUATING: - assert(_state == WAITING_FOR_BOOTSTRAP || _state == MARKING, "Cannot have old collection candidates without first marking, state is '%s'", state_name(_state)); + assert(_state == IDLE || _state == MARKING, "Cannot have old collection candidates without first marking, state is '%s'", state_name(_state)); assert(_old_heuristics->unprocessed_old_collection_candidates() > 0, "Must have collection candidates here."); break; default: @@ -594,10 +580,10 @@ void ShenandoahOldGeneration::validate_transition(State new_state) { } } -bool ShenandoahOldGeneration::validate_waiting_for_bootstrap() { +bool ShenandoahOldGeneration::validate_idle() { ShenandoahHeap* heap = ShenandoahHeap::heap(); - assert(!heap->is_concurrent_old_mark_in_progress(), "Cannot become ready for bootstrap during old mark."); - assert(heap->young_generation()->old_gen_task_queues() == nullptr, "Cannot become ready for bootstrap when still setup for bootstrapping."); + assert(!heap->is_concurrent_old_mark_in_progress(), "Cannot be idle during old mark."); + assert(heap->young_generation()->old_gen_task_queues() == nullptr, "Cannot be idle when still setup for bootstrapping."); assert(!is_concurrent_mark_in_progress(), "Cannot be marking in IDLE"); assert(!heap->young_generation()->is_bootstrap_cycle(), "Cannot have old mark queues if IDLE"); assert(!_old_heuristics->has_coalesce_and_fill_candidates(), "Cannot have coalesce and fill candidates in IDLE"); @@ -699,7 +685,7 @@ void ShenandoahOldGeneration::abandon_collection_candidates() { _old_heuristics->abandon_collection_candidates(); } -void ShenandoahOldGeneration::prepare_for_mixed_collections_after_global_gc() { +void ShenandoahOldGeneration::transition_old_generation_after_global_gc() { assert(is_mark_complete(), "Expected old generation mark to be complete after global cycle."); _old_heuristics->prepare_for_old_collections(); log_info(gc, ergo)("After choosing global collection set, mixed candidates: " UINT32_FORMAT ", coalescing candidates: %zu", @@ -733,7 +719,7 @@ void ShenandoahOldGeneration::set_parsable(bool parsable) { // that we would unload classes and make everything parsable. But, we know // that now so we can override this state. abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); break; default: // We can get here during a full GC. The full GC will cancel anything @@ -750,7 +736,7 @@ void ShenandoahOldGeneration::complete_mixed_evacuations() { assert(is_doing_mixed_evacuations(), "Mixed evacuations should be in progress"); if (!_old_heuristics->has_coalesce_and_fill_candidates()) { // No candidate regions to coalesce and fill - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); return; } @@ -764,7 +750,7 @@ void ShenandoahOldGeneration::complete_mixed_evacuations() { // more to do. assert(state() == ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL, "Should be evacuating after a global cycle"); abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); } void ShenandoahOldGeneration::abandon_mixed_evacuations() { @@ -774,7 +760,7 @@ void ShenandoahOldGeneration::abandon_mixed_evacuations() { break; case ShenandoahOldGeneration::EVACUATING_AFTER_GLOBAL: abandon_collection_candidates(); - transition_to(ShenandoahOldGeneration::WAITING_FOR_BOOTSTRAP); + transition_to(ShenandoahOldGeneration::IDLE); break; default: log_warning(gc)("Abandon mixed evacuations in unexpected state: %s", state_name(state())); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 12e046a1afc..630736190f0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -213,7 +213,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { bool is_concurrent_mark_in_progress() override; bool entry_coalesce_and_fill(); - void prepare_for_mixed_collections_after_global_gc(); + void transition_old_generation_after_global_gc(); void prepare_gc() override; void prepare_regions_and_collection_set(bool concurrent) override; void record_success_concurrent(bool abbreviated) override; @@ -256,11 +256,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { } bool is_idle() const { - return state() == WAITING_FOR_BOOTSTRAP; - } - - bool is_bootstrapping() const { - return state() == BOOTSTRAPPING; + return state() == IDLE; } // Amount of live memory (bytes) in regions waiting for mixed collections @@ -271,11 +267,11 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { public: enum State { - FILLING, WAITING_FOR_BOOTSTRAP, BOOTSTRAPPING, MARKING, EVACUATING, EVACUATING_AFTER_GLOBAL + FILLING, IDLE, MARKING, EVACUATING, EVACUATING_AFTER_GLOBAL }; #ifdef ASSERT - bool validate_waiting_for_bootstrap(); + bool validate_idle(); #endif private: @@ -318,7 +314,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { size_t usage_trigger_threshold() const; bool can_start_gc() { - return _state == WAITING_FOR_BOOTSTRAP; + return _state == IDLE; } static const char* state_name(State state); diff --git a/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp b/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp index bcc757d6132..7b15813678a 100644 --- a/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetAssembler.hpp @@ -34,6 +34,9 @@ class ZBarrierSetAssemblerBase : public BarrierSetAssembler { static Address load_bad_mask_from_jni_env(Register env); static Address mark_bad_mask_from_jni_env(Register env); + + virtual void register_reloc_addresses(GrowableArray
&entries, int begin, int count) { } + virtual void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { } }; // Needs to be included after definition of ZBarrierSetAssemblerBase diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index d80ce4e149d..a439b3a167b 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -33,6 +33,7 @@ #include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" +#include "runtime/icache.hpp" #include "runtime/threadWXSetters.inline.hpp" bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { @@ -70,12 +71,15 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return false; } - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } const uintptr_t prev_color = ZNMethod::color(nm); const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr; diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index 27f352a624f..0f9f4e34a5e 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -58,6 +58,7 @@ #include "prims/jvmtiTagMap.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/safepoint.hpp" #include "runtime/threads.hpp" #include "runtime/vmOperations.hpp" @@ -1434,12 +1435,15 @@ class ZRemapNMethodClosure : public NMethodClosure { virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); - - // Heal oops - ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); + + // Heal oops + ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by old remapping", p2i(nm)); diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index 03701ae9998..ac7d86db240 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -59,6 +59,7 @@ #include "oops/oop.inline.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepointMechanism.hpp" @@ -718,12 +719,15 @@ class ZMarkNMethodClosure : public NMethodClosure { virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); - - // Heal oops - ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); + + // Heal oops + ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } // CodeCache unloading support nm->mark_as_maybe_on_stack(); @@ -753,10 +757,6 @@ class ZMarkYoungNMethodClosure : public NMethodClosure { if (_bs_nm->is_armed(nm)) { const uintptr_t prev_color = ZNMethod::color(nm); - // Heal oops - ZUncoloredRootMarkYoungOopClosure cl(prev_color); - ZNMethod::nmethod_oops_do_inner(nm, &cl); - // Disarm only the young marking, not any potential old marking cycle const uintptr_t old_marked_mask = ZPointerMarkedMask ^ (ZPointerMarkedYoung0 | ZPointerMarkedYoung1); @@ -767,9 +767,16 @@ class ZMarkYoungNMethodClosure : public NMethodClosure { // Check if disarming for young mark, completely disarms the nmethod entry barrier const bool complete_disarm = ZPointer::is_store_good(new_disarm_value_ptr); - if (complete_disarm) { - // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + if (complete_disarm) { + // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming + ZNMethod::nmethod_patch_barriers(nm, &icic); + } + + // Heal oops + ZUncoloredRootMarkYoungOopClosure cl(prev_color); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); } _bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr)); diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 780bc9e3bf7..a1348b63b6f 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" +#include "runtime/icache.hpp" #include "utilities/debug.hpp" static ZNMethodData* gc_data(const nmethod* nm) { @@ -245,8 +246,16 @@ void ZNMethod::set_guard_value(nmethod* nm, int value) { } void ZNMethod::nmethod_patch_barriers(nmethod* nm) { + ICacheInvalidationContext icic; + nmethod_patch_barriers(nm, &icic); +} + +void ZNMethod::nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic) { ZBarrierSetAssembler* const bs_asm = ZBarrierSet::assembler(); ZArrayIterator iter(gc_data(nm)->barriers()); + if (gc_data(nm)->barriers()->is_nonempty()) { + icic->set_has_modified_code(); + } for (ZNMethodDataBarrier barrier; iter.next(&barrier);) { bs_asm->patch_barrier_relocation(barrier._reloc_addr, barrier._reloc_format); } @@ -258,6 +267,11 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { } void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { + ICacheInvalidationContext icic; + nmethod_oops_do_inner(nm, cl, &icic); +} + +void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic) { // Process oops table { oop* const begin = nm->oops_begin(); @@ -283,7 +297,7 @@ void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { // Process non-immediate oops if (data->has_non_immediate_oops()) { - nm->fix_oop_relocations(); + nm->fix_oop_relocations(icic); } } diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 865ea11e7b9..2779151c576 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -56,9 +56,11 @@ class ZNMethod : public AllStatic { static void set_guard_value(nmethod* nm, int value); static void nmethod_patch_barriers(nmethod* nm); + static void nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic); static void nmethod_oops_do(nmethod* nm, OopClosure* cl); static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl); + static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic); static void nmethods_do_begin(bool secondary); static void nmethods_do_end(bool secondary); diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 82d17440c56..dab0ca98911 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -383,6 +383,10 @@ const TypePtr* ArrayCopyNode::get_address_type(PhaseGVN* phase, const TypePtr* a return atp->add_offset(Type::OffsetBot); } +const TypePtr* ArrayCopyNode::get_src_adr_type(PhaseGVN* phase) const { + return get_address_type(phase, _src_type, in(Src)); +} + void ArrayCopyNode::array_copy_test_overlap(PhaseGVN *phase, bool can_reshape, bool disjoint_bases, int count, Node*& forward_ctl, Node*& backward_ctl) { Node* ctl = in(TypeFunc::Control); if (!disjoint_bases && count > 1) { @@ -688,7 +692,7 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { return mem; } -bool ArrayCopyNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { +bool ArrayCopyNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { Node* dest = in(ArrayCopyNode::Dest); if (dest->is_top()) { return false; diff --git a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp index 83c085fd5db..483a3a86267 100644 --- a/src/hotspot/share/opto/arraycopynode.hpp +++ b/src/hotspot/share/opto/arraycopynode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,6 +95,7 @@ class ArrayCopyNode : public CallNode { _arraycopy_type_Type = TypeFunc::make(domain, range); } + const TypePtr* get_src_adr_type(PhaseGVN* phase) const; private: ArrayCopyNode(Compile* C, bool alloc_tightly_coupled, bool has_negative_length_guard); @@ -183,7 +184,7 @@ class ArrayCopyNode : public CallNode { virtual bool guaranteed_safepoint() { return false; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase); + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const; bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; } diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index c7c0810ae85..b2b01079379 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -827,7 +827,7 @@ uint CallNode::match_edge(uint idx) const { // Determine whether the call could modify the field of the specified // instance at the specified offset. // -bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { +bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { assert((t_oop != nullptr), "sanity"); if (is_call_to_arraycopystub() && strcmp(_name, "unsafe_arraycopy") != 0) { const TypeTuple* args = _tf->domain(); @@ -2413,7 +2413,7 @@ void AbstractLockNode::log_lock_optimization(Compile *C, const char * tag, Node* } } -bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) { +bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) const { if (dest_t->is_known_instance() && t_oop->is_known_instance()) { return dest_t->instance_id() == t_oop->instance_id(); } diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index a5131676347..5241ae6cd2b 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -685,7 +685,7 @@ class CallGenerator; class CallNode : public SafePointNode { protected: - bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase); + bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr* t_oop, PhaseValues* phase) const; public: const TypeFunc* _tf; // Function type @@ -734,7 +734,7 @@ class CallNode : public SafePointNode { virtual bool needs_deep_clone_jvms(Compile* C) { return _generator != nullptr || C->needs_deep_clone_jvms(); } // Returns true if the call may modify n - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase); + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const; // Does this node have a use of n other than in debug information? bool has_non_debug_use(Node* n); // Returns the unique CheckCastPP of a call @@ -1044,7 +1044,7 @@ class AllocateNode : public CallNode { virtual bool guaranteed_safepoint() { return false; } // allocations do not modify their arguments - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) { return false;} + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { return false; } // Pattern-match a possible usage of AllocateNode. // Return null if no allocation is recognized. @@ -1208,7 +1208,7 @@ class AbstractLockNode: public CallNode { bool is_balanced(); // locking does not modify its arguments - virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase){ return false; } + virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { return false; } #ifndef PRODUCT void create_lock_counter(JVMState* s); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index e4418631d17..9a1da726f00 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/graphKit.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" @@ -909,8 +910,7 @@ void Parse::catch_call_exceptions(ciExceptionHandlerStream& handlers) { if (handler_bci < 0) { // merge with corresponding rethrow node throw_to_exit(make_exception_state(ex_oop)); } else { // Else jump to corresponding handle - push_ex_oop(ex_oop); // Clear stack and push just the oop. - merge_exception(handler_bci); + push_and_merge_exception(handler_bci, ex_oop); } } @@ -1008,13 +1008,10 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { int handler_bci = handler->handler_bci(); if (remaining == 1) { - push_ex_oop(ex_node); // Push exception oop for handler if (PrintOpto && WizardMode) { tty->print_cr(" Catching every inline exception bci:%d -> handler_bci:%d", bci(), handler_bci); } - // If this is a backwards branch in the bytecodes, add safepoint - maybe_add_safepoint(handler_bci); - merge_exception(handler_bci); // jump to handler + push_and_merge_exception(handler_bci, ex_node); // jump to handler return; // No more handling to be done here! } @@ -1039,15 +1036,13 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { const TypeInstPtr* tinst = TypeOopPtr::make_from_klass_unique(klass)->cast_to_ptr_type(TypePtr::NotNull)->is_instptr(); assert(klass->has_subklass() || tinst->klass_is_exact(), "lost exactness"); Node* ex_oop = _gvn.transform(new CheckCastPPNode(control(), ex_node, tinst)); - push_ex_oop(ex_oop); // Push exception oop for handler if (PrintOpto && WizardMode) { tty->print(" Catching inline exception bci:%d -> handler_bci:%d -- ", bci(), handler_bci); klass->print_name(); tty->cr(); } // If this is a backwards branch in the bytecodes, add safepoint - maybe_add_safepoint(handler_bci); - merge_exception(handler_bci); + push_and_merge_exception(handler_bci, ex_oop); } set_control(not_subtype_ctrl); diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 1c54a11e2a4..01c67187883 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -52,6 +52,7 @@ #include "prims/jvmtiExport.hpp" #include "runtime/continuation.hpp" #include "runtime/sharedRuntime.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" #if INCLUDE_G1GC @@ -261,74 +262,128 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me } } -// Generate loads from source of the arraycopy for fields of -// destination needed at a deoptimization point -Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc) { +// Determine if there is an interfering store between a rematerialization load and an arraycopy that is in the process +// of being elided. Starting from the given rematerialization load this method starts a BFS traversal upwards through +// the memory graph towards the provided ArrayCopyNode. For every node encountered on the traversal, check that it is +// independent from the provided rematerialization. Returns false if every node on the traversal is independent and +// true otherwise. +bool has_interfering_store(const ArrayCopyNode* ac, LoadNode* load, PhaseGVN* phase) { + assert(ac != nullptr && load != nullptr, "sanity"); + AccessAnalyzer acc(phase, load); + ResourceMark rm; + Unique_Node_List to_visit; + to_visit.push(load->in(MemNode::Memory)); + + for (uint worklist_idx = 0; worklist_idx < to_visit.size(); worklist_idx++) { + Node* mem = to_visit.at(worklist_idx); + + if (mem->is_Proj() && mem->in(0) == ac) { + // Reached the target, so visit what is left on the worklist. + continue; + } + + if (mem->is_Phi()) { + assert(mem->bottom_type() == Type::MEMORY, "do not leave memory graph"); + // Add all non-control inputs of phis to be visited. + for (uint phi_in = 1; phi_in < mem->len(); phi_in++) { + Node* input = mem->in(phi_in); + if (input != nullptr) { + to_visit.push(input); + } + } + continue; + } + + AccessAnalyzer::AccessIndependence ind = acc.detect_access_independence(mem); + if (ind.independent) { + to_visit.push(ind.mem); + } else { + return true; + } + } + // Did not find modification of source element in memory graph. + return false; +} + +// Generate loads from source of the arraycopy for fields of destination needed at a deoptimization point. +// Returns nullptr if the load cannot be created because the arraycopy is not suitable for elimination +// (e.g. copy inside the array with non-constant offsets) or the inputs do not match our assumptions (e.g. +// the arraycopy does not actually write something at the provided offset). +Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type* ftype, AllocateNode* alloc) { + assert((ctl == ac->control() && mem == ac->memory()) != (mem != ac->memory() && ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj()), + "Either the control and memory are the same as for the arraycopy or they are pinned in an uncommon trap."); BasicType bt = ft; const Type *type = ftype; if (ft == T_NARROWOOP) { bt = T_OBJECT; type = ftype->make_oopptr(); } - Node* res = nullptr; + Node* base = ac->in(ArrayCopyNode::Src); + Node* adr = nullptr; + const TypePtr* adr_type = nullptr; + if (ac->is_clonebasic()) { assert(ac->in(ArrayCopyNode::Src) != ac->in(ArrayCopyNode::Dest), "clone source equals destination"); - Node* base = ac->in(ArrayCopyNode::Src); - Node* adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); - const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); - MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); + adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); } else { - if (ac->modifies(offset, offset, &_igvn, true)) { - assert(ac->in(ArrayCopyNode::Dest) == alloc->result_cast(), "arraycopy destination should be allocation's result"); - uint shift = exact_log2(type2aelembytes(bt)); - Node* src_pos = ac->in(ArrayCopyNode::SrcPos); - Node* dest_pos = ac->in(ArrayCopyNode::DestPos); - const TypeInt* src_pos_t = _igvn.type(src_pos)->is_int(); - const TypeInt* dest_pos_t = _igvn.type(dest_pos)->is_int(); - - Node* adr = nullptr; - const TypePtr* adr_type = nullptr; - if (src_pos_t->is_con() && dest_pos_t->is_con()) { - intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; - Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); - adr_type = _igvn.type(base)->is_ptr()->add_offset(off); - if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { - // Don't emit a new load from src if src == dst but try to get the value from memory instead - return value_from_mem(ac->in(TypeFunc::Memory), ctl, ft, ftype, adr_type->isa_oopptr(), alloc); - } - } else { - Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos))); + if (!ac->modifies(offset, offset, &_igvn, true)) { + // If the arraycopy does not copy to this offset, we cannot generate a rematerialization load for it. + return nullptr; + } + assert(ac->in(ArrayCopyNode::Dest) == alloc->result_cast(), "arraycopy destination should be allocation's result"); + uint shift = exact_log2(type2aelembytes(bt)); + Node* src_pos = ac->in(ArrayCopyNode::SrcPos); + Node* dest_pos = ac->in(ArrayCopyNode::DestPos); + const TypeInt* src_pos_t = _igvn.type(src_pos)->is_int(); + const TypeInt* dest_pos_t = _igvn.type(dest_pos)->is_int(); + + if (src_pos_t->is_con() && dest_pos_t->is_con()) { + intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); + adr_type = _igvn.type(base)->is_ptr()->add_offset(off); + if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { + // Don't emit a new load from src if src == dst but try to get the value from memory instead + return value_from_mem(ac, ctl, ft, ftype, adr_type->isa_oopptr(), alloc); + } + } else { + Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos))); #ifdef _LP64 - diff = _igvn.transform(new ConvI2LNode(diff)); + diff = _igvn.transform(new ConvI2LNode(diff)); #endif - diff = _igvn.transform(new LShiftXNode(diff, _igvn.intcon(shift))); - - Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); - Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(AddPNode::make_with_base(base, off)); - adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); - if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { - // Non constant offset in the array: we can't statically - // determine the value - return nullptr; - } + diff = _igvn.transform(new LShiftXNode(diff, _igvn.intcon(shift))); + + Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); + adr = _igvn.transform(AddPNode::make_with_base(base, off)); + adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); + if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { + // Non constant offset in the array: we can't statically + // determine the value + return nullptr; } - MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); - BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); - res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemen, adr, adr_type, type, bt); } } - if (res != nullptr) { - if (ftype->isa_narrowoop()) { - // PhaseMacroExpand::scalar_replacement adds DecodeN nodes - res = _igvn.transform(new EncodePNode(res, ftype)); - } - return res; + assert(adr != nullptr && adr_type != nullptr, "sanity"); + + // Create the rematerialization load ... + MergeMemNode* mergemem = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); + BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); + Node* res = ArrayCopyNode::load(bs, &_igvn, ctl, mergemem, adr, adr_type, type, bt); + assert(res != nullptr, "load should have been created"); + + // ... and ensure that pinning the rematerialization load inside the uncommon path is safe. + if (mem != ac->memory() && ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj() && res->is_Load() && + has_interfering_store(ac, res->as_Load(), &_igvn)) { + // Not safe: use control and memory from the arraycopy to ensure correct memory state. + _igvn.remove_dead_node(res, PhaseIterGVN::NodeOrigin::Graph); // Clean up the unusable rematerialization load. + return make_arraycopy_load(ac, offset, ac->control(), ac->memory(), ft, ftype, alloc); } - return nullptr; + + if (ftype->isa_narrowoop()) { + // PhaseMacroExpand::scalar_replacement adds DecodeN nodes + res = _igvn.transform(new EncodePNode(res, ftype)); + } + return res; } // @@ -441,21 +496,22 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } // Search the last value stored into the object's field. -Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc) { +Node* PhaseMacroExpand::value_from_mem(Node* origin, Node* ctl, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc) { assert(adr_t->is_known_instance_field(), "instance required"); int instance_id = adr_t->instance_id(); assert((uint)instance_id == alloc->_idx, "wrong allocation"); int alias_idx = C->get_alias_index(adr_t); int offset = adr_t->offset(); + Node* orig_mem = origin->in(TypeFunc::Memory); Node *start_mem = C->start()->proj_out_or_null(TypeFunc::Memory); Node *alloc_ctrl = alloc->in(TypeFunc::Control); Node *alloc_mem = alloc->proj_out_or_null(TypeFunc::Memory, /*io_use:*/false); assert(alloc_mem != nullptr, "Allocation without a memory projection."); VectorSet visited; - bool done = sfpt_mem == alloc_mem; - Node *mem = sfpt_mem; + bool done = orig_mem == alloc_mem; + Node *mem = orig_mem; while (!done) { if (visited.test_set(mem->_idx)) { return nullptr; // found a loop, give up @@ -535,17 +591,22 @@ Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType } } } else if (mem->is_ArrayCopy()) { - Node* ctl = mem->in(0); - Node* m = mem->in(TypeFunc::Memory); - if (sfpt_ctl->is_Proj() && sfpt_ctl->as_Proj()->is_uncommon_trap_proj()) { + // Rematerialize the scalar-replaced array. If possible, pin the loads to the uncommon path of the uncommon trap. + // Check for each element of the source array, whether it was modified. If not, pin both memory and control to + // the uncommon path. Otherwise, use the control and memory state of the arraycopy. Control and memory state must + // come from the same source to prevent anti-dependence problems in the backend. + ArrayCopyNode* ac = mem->as_ArrayCopy(); + Node* ac_ctl = ac->control(); + Node* ac_mem = ac->memory(); + if (ctl->is_Proj() && ctl->as_Proj()->is_uncommon_trap_proj()) { // pin the loads in the uncommon trap path - ctl = sfpt_ctl; - m = sfpt_mem; + ac_ctl = ctl; + ac_mem = orig_mem; } - return make_arraycopy_load(mem->as_ArrayCopy(), offset, ctl, m, ft, ftype, alloc); + return make_arraycopy_load(ac, offset, ac_ctl, ac_mem, ft, ftype, alloc); } } - // Something go wrong. + // Something went wrong. return nullptr; } @@ -852,7 +913,7 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr(); - Node *field_val = value_from_mem(sfpt->memory(), sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc); + Node* field_val = value_from_mem(sfpt, sfpt->control(), basic_elem_type, field_type, field_addr_type, alloc); // We weren't able to find a value for this field, // give up on eliminating this allocation. diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 59a455cae6d..e4b20402e63 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -104,7 +104,7 @@ class PhaseMacroExpand : public Phase { address slow_call_address, Node* valid_length_test); void yank_alloc_node(AllocateNode* alloc); - Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc); + Node* value_from_mem(Node* start, Node* ctl, BasicType ft, const Type* ftype, const TypeOopPtr* adr_t, AllocateNode* alloc); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level); bool eliminate_boxing_node(CallStaticJavaNode *boxing); @@ -205,7 +205,7 @@ class PhaseMacroExpand : public Phase { Node* klass_node, Node* length, Node* size_in_bytes); - Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type *ftype, AllocateNode *alloc); + Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, Node* mem, BasicType ft, const Type* ftype, AllocateNode* alloc); public: PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) { diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 397a7796f88..42f44f0f7ea 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -474,8 +474,8 @@ class Parse : public GraphKit { void merge( int target_bci); // Same as plain merge, except that it allocates a new path number. void merge_new_path( int target_bci); - // Merge the current mapping into an exception handler. - void merge_exception(int target_bci); + // Push the exception oop and merge the current mapping into an exception handler. + void push_and_merge_exception(int target_bci, Node* ex_oop); // Helper: Merge the current mapping into the given basic block void merge_common(Block* target, int pnum); // Helper functions for merging individual cells. diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 647e8515b30..683633f6355 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1651,9 +1651,14 @@ void Parse::merge_new_path(int target_bci) { } //-------------------------merge_exception------------------------------------- -// Merge the current mapping into the basic block starting at bci -// The ex_oop must be pushed on the stack, unlike throw_to_exit. -void Parse::merge_exception(int target_bci) { +// Push the given ex_oop onto the stack, then merge the current mapping into +// the basic block starting at target_bci. +void Parse::push_and_merge_exception(int target_bci, Node* ex_oop) { + // Add the safepoint before trimming the stack and pushing the exception oop. + // We could add the safepoint after, but then the bci would also need to be + // advanced to target_bci first, so the stack state matches. + maybe_add_safepoint(target_bci); + push_ex_oop(ex_oop); // Push exception oop for handler #ifdef ASSERT if (target_bci <= bci()) { C->set_exception_backedge(); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 06963eff4cf..0fecc14f31a 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -909,9 +909,9 @@ void PhaseIterGVN::verify_step(Node* n) { } } -void PhaseIterGVN::trace_PhaseIterGVN(Node* n, Node* nn, const Type* oldtype) { +void PhaseIterGVN::trace_PhaseIterGVN(Node* n, Node* nn, const Type* oldtype, bool progress) { const Type* newtype = type_or_null(n); - if (nn != n || oldtype != newtype) { + if (progress) { C->print_method(PHASE_AFTER_ITER_GVN_STEP, 5, n); } if (TraceIterativeGVN) { @@ -1092,7 +1092,9 @@ bool PhaseIterGVN::drain_worklist() { NOT_PRODUCT(const Type* oldtype = type_or_null(n)); // Do the transformation DEBUG_ONLY(int live_nodes_before = C->live_nodes();) + NOT_PRODUCT(uint progress_before = made_progress();) Node* nn = transform_old(n); + NOT_PRODUCT(bool progress = (made_progress() - progress_before) > 0;) DEBUG_ONLY(int live_nodes_after = C->live_nodes();) // Ensure we did not increase the live node count with more than // max_live_nodes_increase_per_iteration during the call to transform_old. @@ -1101,7 +1103,7 @@ bool PhaseIterGVN::drain_worklist() { "excessive live node increase in single iteration of IGVN: %d " "(should be at most %d)", increase, max_live_nodes_increase_per_iteration); - NOT_PRODUCT(trace_PhaseIterGVN(n, nn, oldtype);) + NOT_PRODUCT(trace_PhaseIterGVN(n, nn, oldtype, progress);) } else if (!n->is_top()) { remove_dead_node(n, NodeOrigin::Graph); } diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index cef273ff3df..014d16f92f6 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -548,7 +548,7 @@ class PhaseIterGVN : public PhaseGVN { #endif #ifndef PRODUCT - void trace_PhaseIterGVN(Node* n, Node* nn, const Type* old_type); + void trace_PhaseIterGVN(Node* n, Node* nn, const Type* old_type, bool progress); void init_verifyPhaseIterGVN(); void verify_PhaseIterGVN(bool deep_revisit_converged); #endif diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 0d2dbb813bd..c01e8578e43 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -24,6 +24,7 @@ #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "code/nmethod.hpp" @@ -154,7 +155,8 @@ static bool check_compiled_frame(JavaThread* thread) { bool OptoRuntime::generate(ciEnv* env) { C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB) - + // disallow any further c2 stub generation + AOTCodeCache::set_c2_stubs_complete(); return true; } diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 2c2cf053033..519e84281ce 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -72,7 +72,7 @@ class DowncallLinker: AllStatic { bool needs_return_buffer, int captured_state_mask, bool needs_transition) - : StubCodeGenerator(buffer, PrintMethodHandleStubs), + : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), _ret_bt(ret_bt), diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 17ade2c068d..61a8aa84080 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -45,6 +45,21 @@ class outputStream; class stringStream; enum class vmIntrinsicID; +// Helper macro to test and set VM flag and corresponding cpu feature +#define CHECK_CPU_FEATURE(feature_test_fn, feature) \ + if (feature_test_fn()) { \ + if (FLAG_IS_DEFAULT(Use##feature)) { \ + FLAG_SET_DEFAULT(Use##feature, true); \ + } else if (!Use##feature) { \ + clear_feature(CPU_##feature); \ + } \ + } else if (Use##feature) { \ + if (!FLAG_IS_DEFAULT(Use##feature)) { \ + warning(#feature " instructions not available on this CPU"); \ + } \ + FLAG_SET_DEFAULT(Use##feature, false); \ + } + // Abstract_VM_Version provides information about the VM. class Abstract_VM_Version: AllStatic { diff --git a/src/hotspot/share/runtime/icache.hpp b/src/hotspot/share/runtime/icache.hpp index bc153862323..692a876d9a6 100644 --- a/src/hotspot/share/runtime/icache.hpp +++ b/src/hotspot/share/runtime/icache.hpp @@ -129,4 +129,27 @@ class ICacheStubGenerator : public StubCodeGenerator { void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub); }; +class DefaultICacheInvalidationContext : StackObj { + public: + NONCOPYABLE(DefaultICacheInvalidationContext); + + DefaultICacheInvalidationContext() {} + + ~DefaultICacheInvalidationContext() {} + + void set_has_modified_code() {} +}; + +#ifndef PD_ICACHE_INVALIDATION_CONTEXT +#define PD_ICACHE_INVALIDATION_CONTEXT DefaultICacheInvalidationContext +#endif // PD_ICACHE_INVALIDATION_CONTEXT + +class ICacheInvalidationContext final : public PD_ICACHE_INVALIDATION_CONTEXT { + private: + NONCOPYABLE(ICacheInvalidationContext); + + public: + using PD_ICACHE_INVALIDATION_CONTEXT::PD_ICACHE_INVALIDATION_CONTEXT; +}; + #endif // SHARE_RUNTIME_ICACHE_HPP diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index adc49f84358..d820968495e 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -70,6 +70,10 @@ void VM_Version_init(); void icache_init2(); void initialize_stub_info(); // must precede all blob/stub generation void preuniverse_stubs_init(); + +#if INCLUDE_CDS +void stubs_AOTAddressTable_init(); +#endif // INCLUDE_CDS void initial_stubs_init(); jint universe_init(); // depends on codeCache_init and preuniverse_stubs_init @@ -149,13 +153,19 @@ jint init_globals() { AOTCodeCache::init2(); // depends on universe_init, must be before initial_stubs_init AsyncLogWriter::initialize(); +#if INCLUDE_CDS + stubs_AOTAddressTable_init(); // publish external addresses used by stubs + // depends on AOTCodeCache::init2 +#endif // INCLUDE_CDS initial_stubs_init(); // stubgen initial stub routines // stack overflow exception blob is referenced by the interpreter - AOTCodeCache::init_early_stubs_table(); // need this after stubgen initial stubs and before shared runtime initial stubs SharedRuntime::generate_initial_stubs(); gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init continuations_init(); // must precede continuation stub generation - continuation_stubs_init(); // depends on continuations_init + AOTCodeCache::init3(); // depends on stubs_AOTAddressTable_init + // and continuations_init and must + // precede continuation stub generation + continuation_stubs_init(); // depends on continuations_init and AOTCodeCache::init3 #if INCLUDE_JFR SharedRuntime::generate_jfr_stubs(); #endif @@ -164,7 +174,6 @@ jint init_globals() { InterfaceSupport_init(); VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps). SharedRuntime::generate_stubs(); - AOTCodeCache::init_shared_blobs_table(); // need this after generate_stubs SharedRuntime::init_adapter_library(); // do this after AOTCodeCache::init_shared_blobs_table return JNI_OK; } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 917ff5b4545..352c90f913b 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -177,6 +177,11 @@ void SharedRuntime::generate_stubs() { CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception)); generate_deopt_blob(); + +#if INCLUDE_CDS + // disallow any further generation of runtime stubs + AOTCodeCache::set_shared_stubs_complete(); +#endif // INCLUDE_CDS } void SharedRuntime::init_adapter_library() { diff --git a/src/hotspot/share/runtime/stubCodeGenerator.cpp b/src/hotspot/share/runtime/stubCodeGenerator.cpp index 43250c004ca..45e40f4a754 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.cpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.cpp @@ -23,6 +23,7 @@ */ #include "asm/macroAssembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "compiler/disassembler.hpp" #include "oops/oop.inline.hpp" @@ -30,7 +31,9 @@ #include "prims/jvmtiExport.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" - +#if INCLUDE_ZGC +#include "gc/z/zBarrierSetAssembler.hpp" +#endif // INCLUDE_ZGC // Implementation of StubCodeDesc @@ -69,14 +72,16 @@ void StubCodeDesc::print() const { print_on(tty); } StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, bool print_code) { _masm = new MacroAssembler(code); _blob_id = BlobId::NO_BLOBID; + _stub_data = nullptr; _print_code = PrintStubCode || print_code; } -StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, BlobId blob_id, bool print_code) { +StubCodeGenerator::StubCodeGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data, bool print_code) { assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); _masm = new MacroAssembler(code); _blob_id = blob_id; + _stub_data = stub_data; _print_code = PrintStubCode || print_code; } @@ -91,11 +96,92 @@ StubCodeGenerator::~StubCodeGenerator() { #endif } +void StubCodeGenerator::setup_code_desc(const char* name, address start, address end, bool loaded_from_cache) { + StubCodeDesc* cdesc = new StubCodeDesc("StubRoutines", name, start, end); + cdesc->set_disp(uint(start - _masm->code_section()->outer()->insts_begin())); + if (loaded_from_cache) { + cdesc->set_loaded_from_cache(); + } + print_stub_code_desc(cdesc); + // copied from ~StubCodeMark() + Forte::register_stub(cdesc->name(), cdesc->begin(), cdesc->end()); + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated(cdesc->name(), cdesc->begin(), cdesc->end()); + } +} + +// Helper used to restore ranges and handler addresses restored from +// AOT cache. Expects entries to contain 3 * count addresses beginning +// at offset begin which identify start of range, end of range and +// address of handler pc. start and end of range may not be null. +// handler pc may be null in which case it defaults to the +// default_handler. + +void StubCodeGenerator::register_unsafe_access_handlers(GrowableArray
&entries, int begin, int count) { + for (int i = 0; i < count; i++) { + int offset = begin + 3 * i; + address start = entries.at(offset); + address end = entries.at(offset + 1); + address handler = entries.at(offset + 2); + assert(start != nullptr, "sanity"); + assert(end != nullptr, "sanity"); + if (handler == nullptr) { + assert(UnsafeMemoryAccess::common_exit_stub_pc() != nullptr, + "default unsafe handler must be set before registering unsafe rgeionwiht no handler!"); + handler = UnsafeMemoryAccess::common_exit_stub_pc(); + } + UnsafeMemoryAccess::add_to_table(start, end, handler); + } +} + +// Helper used to retrieve ranges and handler addresses registered +// during generation of the stub which spans [start, end) in order to +// allow them to be saved to an AOT cache. +void StubCodeGenerator::retrieve_unsafe_access_handlers(address start, address end, GrowableArray
&entries) { + UnsafeMemoryAccess::collect_entries(start, end, entries); +} + +#if INCLUDE_ZGC +// Helper used to restore ZGC pointer colouring relocation addresses +// retrieved from the AOT cache. +void StubCodeGenerator::register_reloc_addresses(GrowableArray
&entries, int begin, int count) { + LogTarget(Trace, aot, codecache, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + for (int i = begin; i < count; i++) { + ls.print_cr("Registered reloc address " INTPTR_FORMAT, p2i(entries.at(i))); + } + } + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); + zbs->register_reloc_addresses(entries, begin, count); +} + +// Helper used to retrieve ranges and handler addresses registered +// during generation of the stub which spans [start, end) in order to +// allow them to be saved to an AOT cache. +void StubCodeGenerator::retrieve_reloc_addresses(address start, address end, GrowableArray
&entries) { + int l = entries.length(); + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); + zbs->retrieve_reloc_addresses(start, end, entries); + LogTarget(Trace, aot, codecache, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + for (int i = l; i < entries.length(); i++) { + ls.print_cr("retrieved reloc address " INTPTR_FORMAT, p2i(entries.at(i))); + } + } +} +#endif // INCLUDE_ZGC + void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) { // default implementation - do nothing } void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { + print_stub_code_desc(cdesc); +} + +void StubCodeGenerator::print_stub_code_desc(StubCodeDesc* cdesc) { LogTarget(Debug, stubs) lt; if (lt.is_enabled()) { LogStream ls(lt); @@ -119,6 +205,52 @@ void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { } } +address StubCodeGenerator::load_archive_data(StubId stub_id, GrowableArray
*entries, GrowableArray
* extras) { + // punt to stub data if it exists and is not for dumping + if (_stub_data == nullptr || _stub_data->is_dumping()) { + return nullptr; + } + // punt to stub data + address start, end; + start = _stub_data->load_archive_data(stub_id, end, entries, extras); + + if (start != nullptr) { + setup_code_desc(StubInfo::name(stub_id), start, end, true); + } + + return start; +} + +void StubCodeGenerator::store_archive_data(StubId stub_id, address start, address end, GrowableArray
* entries, GrowableArray
* extras) { + // punt to stub data if we have any + if (_stub_data != nullptr) { + _stub_data->store_archive_data(stub_id, start, end, entries, extras); + } +} + +void StubCodeGenerator::print_statistics_on(outputStream* st) { + st->print_cr("StubRoutines Stubs:"); + st->print_cr(" Initial stubs: %d", StubInfo::stub_count(BlobId::stubgen_initial_id)); + st->print_cr(" Continuation stubs: %d", StubInfo::stub_count(BlobId::stubgen_continuation_id)); + st->print_cr(" Compiler stubs: %d", StubInfo::stub_count(BlobId::stubgen_compiler_id)); + st->print_cr(" Final stubs: %d", StubInfo::stub_count(BlobId::stubgen_final_id)); + + int emitted = 0; + int loaded_from_cache = 0; + + StubCodeDesc* scd = StubCodeDesc::first(); + while (scd != nullptr) { + if (!strcmp(scd->group(), "StubRoutines")) { + emitted += 1; + if (scd->loaded_from_cache()) { + loaded_from_cache += 1; + } + } + scd = StubCodeDesc::next(scd); + } + st->print_cr("Total stubroutines stubs emitted: %d (generated=%d, loaded from cache=%d)", emitted, emitted - loaded_from_cache, loaded_from_cache); +} + #ifdef ASSERT void StubCodeGenerator::verify_stub(StubId stub_id) { assert(StubRoutines::stub_to_blob(stub_id) == blob_id(), "wrong blob %s for generation of stub %s", StubRoutines::get_blob_name(blob_id()), StubRoutines::get_stub_name(stub_id)); diff --git a/src/hotspot/share/runtime/stubCodeGenerator.hpp b/src/hotspot/share/runtime/stubCodeGenerator.hpp index 7d8944c85ea..958fa76543b 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.hpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.hpp @@ -26,6 +26,7 @@ #define SHARE_RUNTIME_STUBCODEGENERATOR_HPP #include "asm/assembler.hpp" +#include "code/aotCodeCache.hpp" #include "memory/allocation.hpp" #include "runtime/stubInfo.hpp" @@ -48,6 +49,7 @@ class StubCodeDesc: public CHeapObj { address _begin; // points to the first byte of the stub code (included) address _end; // points to the first byte after the stub code (excluded) uint _disp; // Displacement relative base address in buffer. + bool _loaded_from_cache; friend class StubCodeMark; friend class StubCodeGenerator; @@ -65,6 +67,8 @@ class StubCodeDesc: public CHeapObj { void set_disp(uint disp) { _disp = disp; } + void set_loaded_from_cache() { _loaded_from_cache = true; } + public: static StubCodeDesc* first() { return _list; } static StubCodeDesc* next(StubCodeDesc* desc) { return desc->_next; } @@ -81,6 +85,7 @@ class StubCodeDesc: public CHeapObj { _end = end; _disp = 0; _list = this; + _loaded_from_cache = false; }; static void freeze(); @@ -93,12 +98,11 @@ class StubCodeDesc: public CHeapObj { uint disp() const { return _disp; } int size_in_bytes() const { return pointer_delta_as_int(_end, _begin); } bool contains(address pc) const { return _begin <= pc && pc < _end; } + bool loaded_from_cache() const { return _loaded_from_cache; } void print_on(outputStream* st) const; void print() const; }; -// forward declare blob and stub id enums - // The base class for all stub-generating code generators. // Provides utility functions. @@ -108,10 +112,20 @@ class StubCodeGenerator: public StackObj { BlobId _blob_id; protected: MacroAssembler* _masm; - - public: + AOTStubData* _stub_data; + + void setup_code_desc(const char* name, address start, address end, bool loaded_from_cache); + // unsafe handler management + void register_unsafe_access_handlers(GrowableArray
&entries, int begin, int count); + void retrieve_unsafe_access_handlers(address start, address end, GrowableArray
&entries); +#if INCLUDE_ZGC + void register_reloc_addresses(GrowableArray
&entries, int begin, int count); + void retrieve_reloc_addresses(address start, address end, GrowableArray
&entries); +#endif // INCLUDE_ZGC + +public: StubCodeGenerator(CodeBuffer* code, bool print_code = false); - StubCodeGenerator(CodeBuffer* code, BlobId blob_id, bool print_code = false); + StubCodeGenerator(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data = nullptr, bool print_code = false); ~StubCodeGenerator(); MacroAssembler* assembler() const { return _masm; } @@ -120,9 +134,59 @@ class StubCodeGenerator: public StackObj { virtual void stub_prolog(StubCodeDesc* cdesc); // called by StubCodeMark constructor virtual void stub_epilog(StubCodeDesc* cdesc); // called by StubCodeMark destructor + void print_stub_code_desc(StubCodeDesc* cdesc); + + static void print_statistics_on(outputStream* st); + + // load_archive_data should be called before generating the stub + // identified by stub_id. If AOT caching of stubs is enabled and the + // stubis found then the address of the stub's first and, possibly, + // only entry is returned and the caller should use it instead of + // generating thestub. Otherwise a null address is returned and the + // caller should proceed to generate the stub. + // + // store_archive_data should be called when a stub has been + // successfully generated into the current blob irrespctive of + // whether the current JVM is generating or consuming an AOT archive + // (the caller should not check for either case). When generating an + // archive the stub entry and end addresses are recorded for storage + // along with the current blob and also to allow rences to the stub + // from other stubs or from compiled Java methods can be detected + // and marked as requiring relocation. When consuming an archive the + // stub entry address is still inorer to identify it as a relocation + // target. When no archive is in use the call has no side effects. + // + // start and end identify the inclusive start and exclusive end + // address for stub code and must lie in the current blob's code + // range. Stubs presented via this interface must declare at least + // one entry and start is always taken to be the first entry. + // + // Optional arrays entries and extras store other addresses of + // interest all of which must either lie in the interval (start, + // end) or be nullptr (verified by load and store methods). + // + // entries lists secondary entries for the stub each of which must + // match a corresponding entry declaration for the stub (entry count + // verified by load and store methods). Null entry addresses are + // allowed when an architecture does not require a specific entry + // but may not vary from one run to the next. If the cache is in use + // at a store (for loading or saving code) then non-null entry + // addresses are entered into the AOT cache stub address table + // allowing references to them from other stubs or nmethods to be + // relocated. + // + // extras lists other non-entry stub addresses of interest such as + // memory protection ranges and associated handler addresses + // (potentially including a null address). These do do not need to + // be declared as entries and their number and meaning may vary + // according to the architecture. + + address load_archive_data(StubId stub_id, GrowableArray
*entries = nullptr, GrowableArray
* extras = nullptr); + void store_archive_data(StubId stub_id, address start, address end, GrowableArray
*entries = nullptr, GrowableArray
* extras = nullptr); #ifdef ASSERT void verify_stub(StubId stub_id); #endif + }; // Stack-allocated helper class used to associate a stub code with a name. diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index 7dc0f2d2bed..d1ce378ee20 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -952,9 +952,15 @@ do_entry_init(final, arrayof_jlong_arraycopy, \ arrayof_jlong_arraycopy, arrayof_jlong_arraycopy, \ StubRoutines::arrayof_jlong_copy) \ + do_entry(final, arrayof_jlong_arraycopy, \ + arrayof_jlong_arraycopy_nopush, \ + arrayof_jlong_arraycopy_nopush) \ do_stub(final, arrayof_oop_arraycopy) \ do_entry_init(final, arrayof_oop_arraycopy, arrayof_oop_arraycopy, \ arrayof_oop_arraycopy, StubRoutines::arrayof_oop_copy) \ + do_entry(final, arrayof_oop_arraycopy, \ + arrayof_oop_arraycopy_nopush, \ + arrayof_oop_arraycopy_nopush) \ do_stub(final, arrayof_oop_arraycopy_uninit) \ do_entry_init(final, arrayof_oop_arraycopy_uninit, \ arrayof_oop_arraycopy_uninit, \ diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index ee90631145a..c07c0298f2b 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -1087,6 +1087,15 @@ int StubInfo::stubgen_offset(StubId id) { return local_offset(StubGroup::STUBGEN, id); } +int StubInfo::stubgen_offset_in_blob(BlobId blob_id, StubId id) { + assert(blob(id) == blob_id, "sanity!"); + StubGroup group = StubGroup::STUBGEN; + assert(stubgroup(blob_id) == group, "sanity"); + StubId base_id = stub_base(blob_id); + assert(base_id != StubId::NO_STUBID, "sanity"); + return local_offset(group, id) - local_offset(group, base_id); +} + // initialization function called to populate blob. stub and entry // tables. this must be called before any stubs are generated void initialize_stub_info() { diff --git a/src/hotspot/share/runtime/stubInfo.hpp b/src/hotspot/share/runtime/stubInfo.hpp index 9ed6e0cb9f9..447f7d3a582 100644 --- a/src/hotspot/share/runtime/stubInfo.hpp +++ b/src/hotspot/share/runtime/stubInfo.hpp @@ -669,6 +669,11 @@ class StubInfo: AllStatic { static int c1_offset(StubId id); static int c2_offset(StubId id); static int stubgen_offset(StubId id); + + // Convert a stub id to a unique, zero-based offset in the range of + // stub ids for a given blob in the stubgen stub group. + + static int stubgen_offset_in_blob(BlobId blob_id, StubId id); }; diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 56d832d3fef..0bc71c65471 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -102,8 +102,7 @@ BlobId StubRoutines::stub_to_blob(StubId id) { // Initialization -extern void StubGenerator_generate(CodeBuffer* code, BlobId blob_id); // only interface to generators - +extern void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data); // only interface to generators void UnsafeMemoryAccess::create_table(int max_size) { UnsafeMemoryAccess::_table = new UnsafeMemoryAccess[max_size]; UnsafeMemoryAccess::_table_max_length = max_size; @@ -154,7 +153,8 @@ void UnsafeMemoryAccess::collect_entries(address range_start, address range_end, if (e._error_exit_pc != _common_exit_stub_pc) { entries.append(e._error_exit_pc); } else { - // an address outside the stub must be the common exit stub address + // an address outside the stub must be the common exit stub + // address which is marked with a null address entries.append(nullptr); } } @@ -169,6 +169,33 @@ static BufferBlob* initialize_stubs(BlobId blob_id, assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); ResourceMark rm; TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); + // If we are loading stubs we need to check if we can retrieve a + // blob and/or an associated archived stub descriptor from the + // AOTCodeCache. If we are storing stubs we need to create a blob + // but we still need a stub data descriptor to fill in during + // generation. + AOTStubData stub_data(blob_id); + AOTStubData* stub_data_p = nullptr; + LogTarget(Info, stubs) lt; + + if (code_size > 0 && stub_data.is_using()) { + // AOTCodeEntry tracks and logs status of any cached blob + bool loaded = stub_data.load_code_blob(); + if (loaded) { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Found blob %s in AOT cache", StubInfo::name(blob_id)); + } + stub_data_p = &stub_data; + } + } else if (stub_data.is_dumping()) { + stub_data_p = &stub_data; + } + + // Even if we managed to load a blob from the AOT cache we still + // need to allocate a code blob and associated buffer. The AOT blob + // may not include all the stubs we need for this runtime. + // Add extra space for large CodeEntryAlignment int size = code_size + CodeEntryAlignment * max_aligned_stubs; BufferBlob* stubs_code = BufferBlob::create(buffer_name, size); @@ -178,6 +205,10 @@ static BufferBlob* initialize_stubs(BlobId blob_id, // In that case we can tolerate an allocation failure because the // compiler will have been shut down and we have no need of the // blob. + // TODO: Ideally we would still like to try to use any AOT cached + // blob here but we don't have a fallback if we find that it is + // missing stubs we need so for now we exit. This should only + // happen in cases where we have a very small code cache. if (Thread::current()->is_Compiler_thread()) { assert(blob_id == BlobId::stubgen_compiler_id, "sanity"); assert(DelayCompilerStubsGeneration, "sanity"); @@ -187,10 +218,12 @@ static BufferBlob* initialize_stubs(BlobId blob_id, vm_exit_out_of_memory(code_size, OOM_MALLOC_ERROR, "CodeCache: no room for %s", buffer_name); } CodeBuffer buffer(stubs_code); - StubGenerator_generate(&buffer, blob_id); + short buffer_locs[20]; + buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs, + sizeof(buffer_locs)/sizeof(relocInfo)); + StubGenerator_generate(&buffer, blob_id, stub_data_p); if (code_size == 0) { assert(buffer.insts_size() == 0, "should not write into buffer when bob size declared as 0"); - LogTarget(Info, stubs) lt; if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("%s\t not generated", buffer_name); @@ -203,7 +236,35 @@ static BufferBlob* initialize_stubs(BlobId blob_id, "increase %s, code_size: %d, used: %d, free: %d", assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); - LogTarget(Info, stubs) lt; + if (stub_data.is_using()) { + // we generated some new entries so republish all entries TODO - + // ensure we publish collect and publish the preuniverse stubs but + // don't try to save them + AOTCodeCache::publish_stub_addresses(*stubs_code, blob_id, &stub_data); + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Republished entries for blob '%s'", buffer_name); + } + } else if (stub_data.is_dumping()) { + // save the blob and publihs the entry addresses + if (stub_data.store_code_blob(*stubs_code, &buffer)) { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Stored blob '%s' to Startup Code Cache", buffer_name); + } + } else { + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Failed to store blob '%s' to Startup Code Cache", buffer_name); + } + } + } + + // close off recording of any further stubgen generation + if (blob_id == BlobId::stubgen_final_id) { + AOTCodeCache::set_stubgen_stubs_complete(); + } + if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("%s\t [" INTPTR_FORMAT ", " INTPTR_FORMAT "] used: %d, free: %d", @@ -214,6 +275,8 @@ static BufferBlob* initialize_stubs(BlobId blob_id, return stubs_code; } +// per blob initializer methods StubRoutines::initialize_xxx_stubs() + #define DEFINE_BLOB_INIT_METHOD(blob_name) \ void StubRoutines::initialize_ ## blob_name ## _stubs() { \ if (STUBGEN_BLOB_FIELD_NAME(blob_name) == nullptr) { \ @@ -234,6 +297,7 @@ STUBGEN_BLOBS_DO(DEFINE_BLOB_INIT_METHOD) #undef DEFINE_BLOB_INIT_METHOD +// external driver API functions for per blob init: xxx_stubs_init() #define DEFINE_BLOB_INIT_FUNCTION(blob_name) \ void blob_name ## _stubs_init() { \ @@ -244,11 +308,18 @@ STUBGEN_BLOBS_DO(DEFINE_BLOB_INIT_FUNCTION) #undef DEFINE_BLOB_INIT_FUNCTION + +#if INCLUDE_CDS +// non-generated external API init driver function + +void stubs_AOTAddressTable_init() { StubRoutines::init_AOTAddressTable(); } +#endif // INCLUDE_CDS + /* - * we generate the underlying driver method but this wrapper is needed - * to perform special handling depending on where the compiler init - * gets called from. it ought to be possible to remove this at some - * point and have a determinate ordered init. + * we generate the underlying driver function compiler_stubs_init() + * but this wrapper is needed to perform special handling depending on + * where the compiler init gets called from. it ought to be possible + * to remove this at some point and have a determinate ordered init. */ void compiler_stubs_init(bool in_compiler_thread) { diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 97e3e46b870..894bd47faab 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -112,6 +112,8 @@ class UnsafeMemoryAccess : public CHeapObj { address _end_pc; address _error_exit_pc; public: + // each table entry requires 3 addresses + static const int COLUMN_COUNT = 3; static address _common_exit_stub_pc; static UnsafeMemoryAccess* _table; static int _table_length; @@ -130,6 +132,7 @@ class UnsafeMemoryAccess : public CHeapObj { static UnsafeMemoryAccess* add_to_table(address start_pc, address end_pc, address error_exit_pc) { guarantee(_table_length < _table_max_length, "Incorrect UnsafeMemoryAccess::_table_max_length"); UnsafeMemoryAccess* entry = &_table[_table_length]; + assert(start_pc != nullptr, "invalid start address"); entry->set_start_pc(start_pc); entry->set_end_pc(end_pc); entry->set_error_exit_pc(error_exit_pc); @@ -283,6 +286,11 @@ class StubRoutines: AllStatic { static BlobId stub_to_blob(StubId id); #endif +#if INCLUDE_CDS + // AOT Initalization -- implementation is arch-specific + static void init_AOTAddressTable(); +#endif // INCLUDE_CDS + // Debugging static jint verify_oop_count() { return _verify_oop_count; } static jint* verify_oop_count_addr() { return &_verify_oop_count; } diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index c6c08ed4473..760f3ebc255 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -70,9 +70,9 @@ * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. *

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

  *     String str = "abc";
  * 

diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index ba73e5b5a86..4c57214dbba 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; -import static java.text.DateFormatSymbols.*; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -57,6 +57,8 @@ import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.TimeZoneNameUtility; +import static java.text.DateFormatSymbols.*; + /** * {@code SimpleDateFormat} is a concrete class for formatting and * parsing dates in a locale-sensitive manner. It allows for formatting @@ -1293,15 +1295,22 @@ private void subFormat(int patternCharIndex, int count, case PATTERN_ZONE_NAME: // 'z' if (current == null) { + TimeZone tz = calendar.getTimeZone(); + String tzid = tz.getID(); + int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); + int dstOffset = calendar.get(Calendar.DST_OFFSET) + zoneOffset; + + // Check if an explicit metazone DST offset exists + String explicitDstOffset = TimeZoneNameUtility.explicitDstOffset(tzid); + boolean daylight = explicitDstOffset != null ? + dstOffset == ZoneOffset.of(explicitDstOffset).getTotalSeconds() * 1_000 : + dstOffset != zoneOffset; if (formatData.locale == null || formatData.isZoneStringsSet) { - int zoneIndex = - formatData.getZoneIndex(calendar.getTimeZone().getID()); + int zoneIndex = formatData.getZoneIndex(tzid); if (zoneIndex == -1) { - value = calendar.get(Calendar.ZONE_OFFSET) + - calendar.get(Calendar.DST_OFFSET); - buffer.append(ZoneInfoFile.toCustomID(value)); + buffer.append(ZoneInfoFile.toCustomID(dstOffset)); } else { - int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3; + int index = daylight ? 3 : 1; if (count < 4) { // Use the short name index++; @@ -1310,8 +1319,6 @@ private void subFormat(int patternCharIndex, int count, buffer.append(zoneStrings[zoneIndex][index]); } } else { - TimeZone tz = calendar.getTimeZone(); - boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0); int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG); buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale)); } diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 4708094effb..4594dc6f1dc 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -4513,7 +4513,11 @@ public boolean format(DateTimePrintContext context, StringBuilder buf, boolean o TemporalAccessor dt = context.getTemporal(); int type = GENERIC; if (!isGeneric) { - if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { + // Check if an explicit metazone DST offset exists + String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); + if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { + type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; + } else if (dt.isSupported(ChronoField.INSTANT_SECONDS)) { type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; } else if (dt.isSupported(ChronoField.EPOCH_DAY) && dt.isSupported(ChronoField.NANO_OF_DAY)) { diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 682476d8082..6b071cd15b2 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1932,7 +1932,7 @@ public String getISO3Country() throws MissingResourceException { } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -1946,14 +1946,15 @@ public String getISO3Country() throws MissingResourceException { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a language, this function returns the empty string. * - * @return The name of the display language. + * @return The name of the display language appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayLanguage() { + public String getDisplayLanguage() { return getDisplayLanguage(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's language that is appropriate for display to the + * Returns a name for {@code this} locale's language that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -1964,7 +1965,7 @@ public final String getDisplayLanguage() { * on the ISO code as a last-resort value. If the locale doesn't specify a language, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display language. + * @param inLocale The locale in which to localize the display language. * @return The name of the display language appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -1973,13 +1974,13 @@ public String getDisplayLanguage(Locale inLocale) { } /** - * Returns a name for the locale's script that is appropriate for display to + * Returns a name for {@code this} locale's script that is appropriate for display to * the user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. Returns * the empty string if this locale doesn't specify a script code. * - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @return The display name of the script code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. * @since 1.7 */ public String getDisplayScript() { @@ -1987,14 +1988,13 @@ public String getDisplayScript() { } /** - * Returns a name for the locale's script that is appropriate + * Returns a name for {@code this} locale's script that is appropriate * for display to the user. If possible, the name will be * localized for the given locale. Returns the empty string if * this locale doesn't specify a script code. * - * @param inLocale The locale for which to retrieve the display script. - * @return the display name of the script code for the current default - * {@link Locale.Category#DISPLAY DISPLAY} locale + * @param inLocale The locale in which to localize the display script. + * @return The display name of the script code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} * @since 1.7 */ @@ -2003,7 +2003,7 @@ public String getDisplayScript(Locale inLocale) { } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. @@ -2017,14 +2017,15 @@ public String getDisplayScript(Locale inLocale) { * this function falls back on the English name, and uses the ISO code as a last-resort * value. If the locale doesn't specify a country, this function returns the empty string. * - * @return The name of the country appropriate to the locale. + * @return The name of the country appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayCountry() { + public String getDisplayCountry() { return getDisplayCountry(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's country that is appropriate for display to the + * Returns a name for {@code this} locale's country that is appropriate for display to the * user. * If possible, the name returned will be localized according to inLocale. * For example, if the locale is fr_FR and inLocale @@ -2035,7 +2036,7 @@ public final String getDisplayCountry() { * on the ISO code as a last-resort value. If the locale doesn't specify a country, * this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display country. + * @param inLocale The locale in which to localize the display country. * @return The name of the country appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2061,23 +2062,24 @@ private String getDisplayString(String code, String cat, Locale inLocale, int ty } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for the default * {@link Locale.Category#DISPLAY DISPLAY} locale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @return The name of the display variant code appropriate to the locale. + * @return The name of the display variant code appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayVariant() { + public String getDisplayVariant() { return getDisplayVariant(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale's variant code that is appropriate for display to the + * Returns a name for {@code this} locale's variant code that is appropriate for display to the * user. If possible, the name will be localized for inLocale. If the locale * doesn't specify a variant code, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display variant code. + * @param inLocale The locale in which to localize the display variant code. * @return The name of the display variant code appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ @@ -2098,7 +2100,7 @@ public String getDisplayVariant(Locale inLocale) { } /** - * Returns a name for the locale that is appropriate for display to the + * Returns a name for {@code this} locale that is appropriate for display to the * user. This will be the values returned by getDisplayLanguage(), * getDisplayScript(), getDisplayCountry(), getDisplayVariant() and * optional {@linkplain ##def_locale_extension Unicode extensions} @@ -2116,14 +2118,15 @@ public String getDisplayVariant(Locale inLocale) { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @return The name of the locale appropriate to display. + * @return The display name appropriate to the default + * {@link Locale.Category#DISPLAY DISPLAY} locale. */ - public final String getDisplayName() { + public String getDisplayName() { return getDisplayName(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale that is appropriate for display + * Returns a name for {@code this} locale that is appropriate for display * to the user. This will be the values returned by * getDisplayLanguage(), getDisplayScript(), getDisplayCountry(), * getDisplayVariant(), and optional {@linkplain ##def_locale_extension @@ -2142,8 +2145,8 @@ public final String getDisplayName() { * be localized depending on the locale. If the language, script, country, * and variant fields are all empty, this function returns the empty string. * - * @param inLocale The locale for which to retrieve the display name. - * @return The name of the locale appropriate to display. + * @param inLocale The locale in which to localize the display name. + * @return The display name appropriate to the given locale. * @throws NullPointerException if {@code inLocale} is {@code null} */ public String getDisplayName(Locale inLocale) { diff --git a/src/java.base/share/classes/java/util/ResourceBundle.java b/src/java.base/share/classes/java/util/ResourceBundle.java index db19eda6399..f91db79891b 100644 --- a/src/java.base/share/classes/java/util/ResourceBundle.java +++ b/src/java.base/share/classes/java/util/ResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1119,15 +1119,15 @@ public static final ResourceBundle getBundle(String baseName, Locale targetLocal * sequence of bundle names generated by truncating the last underscore and * the part following it is inserted after a candidate bundle name with the * original variant. For example, for a locale with language "en", script - * "Latn, country "US" and variant "WINDOWS_VISTA", and bundle base name + * "Latn", country "US" and variant "WINDOWS_WIN11", and bundle base name * "MyResource", the list of candidate bundle names below is generated: * *

-     * MyResource_en_Latn_US_WINDOWS_VISTA
+     * MyResource_en_Latn_US_WINDOWS_WIN11
      * MyResource_en_Latn_US_WINDOWS
      * MyResource_en_Latn_US
      * MyResource_en_Latn
-     * MyResource_en_US_WINDOWS_VISTA
+     * MyResource_en_US_WINDOWS_WIN11
      * MyResource_en_US_WINDOWS
      * MyResource_en_US
      * MyResource_en
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index 3a915cf96df..480553e9a62 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
@@ -1924,9 +1924,15 @@ private void doTunneling0() throws IOException {
                 }
 
                 statusLine = responses.getValue(0);
-                StringTokenizer st = new StringTokenizer(statusLine);
-                st.nextToken();
-                respCode = Integer.parseInt(st.nextToken().trim());
+                respCode = parseConnectResponseCode(statusLine);
+                if (respCode == -1) {
+                    // a respCode of -1, due to a invalid status line,
+                    // will (rightly) result in an IOException being thrown
+                    // later in this code. here we merely log the invalid status line.
+                    if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+                        logger.fine("invalid status line: \"" + statusLine + "\"");
+                    }
+                }
                 if (respCode == HTTP_PROXY_AUTH) {
                     // Read comments labeled "Failed Negotiate" for details.
                     boolean dontUseNegotiate = false;
@@ -2027,6 +2033,37 @@ private void doTunneling0() throws IOException {
         responses.reset();
     }
 
+    // parses the status line, that was returned for a CONNECT request, and returns
+    // the response code from that line. returns -1 if the response code could not be
+    // parsed.
+    private static int parseConnectResponseCode(final String statusLine) {
+        final int invalidStatusLine = -1;
+        if (statusLine == null || statusLine.isBlank()) {
+            return invalidStatusLine;
+        }
+        //
+        // status-line = HTTP-version SP status-code SP [ reason-phrase ]
+        // SP = space character
+        //
+        final StringTokenizer st = new StringTokenizer(statusLine, " ");
+        if (!st.hasMoreTokens()) {
+            return invalidStatusLine;
+        }
+        st.nextToken(); // the HTTP version part (ex: HTTP/1.1)
+        if (!st.hasMoreTokens()) {
+            return invalidStatusLine;
+        }
+        final String v = st.nextToken().trim(); // status code
+        try {
+            return Integer.parseInt(v);
+        } catch (NumberFormatException nfe) {
+            if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+                logger.fine("invalid response code: " + v);
+            }
+        }
+        return invalidStatusLine;
+    }
+
     /**
      * Overridden in https to also include the server certificate
      */
diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
index dea86351cc8..0ca197160a9 100644
--- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
+++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
@@ -49,6 +49,9 @@
  */
 public class KAKeyDerivation implements SSLKeyDerivation {
 
+    // Algorithm used to derive TLS 1.3 shared secrets
+    private static final String t13KeyDerivationAlgorithm =
+            System.getProperty("jdk.tls.t13KeyDerivationAlgorithm", "Generic");
     private final String algorithmName;
     private final HandshakeContext context;
     private final PrivateKey localPrivateKey;
@@ -234,7 +237,7 @@ private SecretKey t13DeriveKey(String type)
                     var decapsulator = kem.newDecapsulator(localPrivateKey);
                     sharedSecret = decapsulator.decapsulate(
                             keyshare, 0, decapsulator.secretSize(),
-                            "TlsPremasterSecret");
+                            t13KeyDerivationAlgorithm);
                 } catch (IllegalArgumentException | InvalidKeyException |
                         DecapsulateException e) {
                     // Peer validation failure
@@ -252,7 +255,7 @@ private SecretKey t13DeriveKey(String type)
                 KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
                 ka.init(localPrivateKey);
                 ka.doPhase(peerPublicKey, true);
-                sharedSecret = ka.generateSecret("TlsPremasterSecret");
+                sharedSecret = ka.generateSecret(t13KeyDerivationAlgorithm);
             }
 
             return deriveHandshakeSecret(type, sharedSecret);
diff --git a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
index 74975fc1e5b..b5f1eff179c 100644
--- a/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/QuicTLSEngineImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -660,7 +660,7 @@ private void handleHandshakeMessage(KeySpace keySpace, ByteBuffer message)
             }
             Alert alert = ((QuicEngineOutputRecord)
                     conContext.outputRecord).getAlert();
-            throw new QuicTransportException(alert.description, keySpace, 0,
+            throw new QuicTransportException(e.getMessage(), keySpace, 0,
                     BASE_CRYPTO_ERROR + alert.id, e);
         } catch (IOException e) {
             throw new RuntimeException(e);
diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
index ac43b22a3bd..76b383c03e1 100644
--- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
+++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java
@@ -105,6 +105,9 @@ public class LocaleResources {
     // TimeZoneNamesBundle exemplar city prefix
     private static final String TZNB_EXCITY_PREFIX = "timezone.excity.";
 
+    // TimeZoneNamesBundle explicit metazone dst offset prefix
+    private static final String TZNB_METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset.";
+
     // null singleton cache value
     private static final Object NULLOBJECT = new Object();
 
@@ -321,7 +324,8 @@ public Object getTimeZoneNames(String key) {
 
         if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
             TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
-            if (key.startsWith(TZNB_EXCITY_PREFIX)) {
+            if (key.startsWith(TZNB_EXCITY_PREFIX) ||
+                key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) {
                 if (tznb.containsKey(key)) {
                     val = tznb.getString(key);
                     assert val instanceof String;
@@ -378,7 +382,8 @@ String[][] getZoneStrings() {
         Set value = new LinkedHashSet<>();
         Set tzIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
         for (String key : keyset) {
-            if (!key.startsWith(TZNB_EXCITY_PREFIX)) {
+            if (!key.startsWith(TZNB_EXCITY_PREFIX) &&
+                !key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) {
                 value.add(rb.getStringArray(key));
                 tzIds.remove(key);
             }
diff --git a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
index fd3d4965db3..6c684e176c8 100644
--- a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
+++ b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java
@@ -37,7 +37,7 @@
 import java.util.spi.TimeZoneNameProvider;
 import sun.util.calendar.ZoneInfo;
 import sun.util.cldr.CLDRLocaleProviderAdapter;
-import static sun.util.locale.provider.LocaleProviderAdapter.Type;
+import static sun.util.locale.provider.LocaleProviderAdapter.Type.CLDR;
 
 /**
  * Utility class that deals with the localized time zone names
@@ -169,10 +169,22 @@ public static Optional convertLDMLShortID(String shortID) {
      * Returns the canonical ID for the given ID
      */
     public static Optional canonicalTZID(String id) {
-        return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR))
+        return ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(CLDR))
                     .canonicalTZID(id);
     }
 
+    /**
+     * {@return the explicit metazone DST offset for the specified time zone ID, if exists}
+     * @param tzid the time zone ID
+     */
+    public static String explicitDstOffset(String tzid) {
+        return (String) (LocaleProviderAdapter.forType(CLDR) instanceof CLDRLocaleProviderAdapter ca ?
+            ca.getLocaleResources(Locale.ROOT)
+                .getTimeZoneNames("metazone.dstoffset." +
+                    ca.canonicalTZID(tzid).orElse(tzid)) :
+            null);
+    }
+
     private static String[] retrieveDisplayNamesImpl(String id, Locale locale) {
         LocaleServiceProviderPool pool =
             LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class);
diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
index a30b84c6872..c5e95c8a404 100644
--- a/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
+++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNamesBundle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,8 +43,6 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
-import java.util.MissingResourceException;
-import java.util.Objects;
 import java.util.Set;
 
 /**
diff --git a/src/java.desktop/share/classes/java/awt/MouseInfo.java b/src/java.desktop/share/classes/java/awt/MouseInfo.java
index 6b913adf06e..7a30243b06c 100644
--- a/src/java.desktop/share/classes/java/awt/MouseInfo.java
+++ b/src/java.desktop/share/classes/java/awt/MouseInfo.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@ private MouseInfo() {
      *
      * @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
      * @return    location of the mouse pointer
+     * @see       GraphicsConfiguration
      * @since     1.5
      */
     public static PointerInfo getPointerInfo() throws HeadlessException {
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
index 5e2384dce27..3fc013b4fde 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/ConnectionTerminatorImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -203,6 +203,9 @@ private void unregisterConnFromEndpoint() {
             // an endpoint has been established (which is OK)
             return;
         }
+        // close the connection ID managers; any in-flight connection ID changes should be ignored.
+        connection.localConnectionIdManager().close();
+        connection.peerConnectionIdManager().close();
         endpoint.removeConnection(this.connection);
     }
 
@@ -434,6 +437,9 @@ private void pushConnectionCloseFrame(final KeySpace keySpace,
         final QuicPacket packet = connection.newQuicPacket(keySpace, List.of(toSend));
         final ProtectionRecord protectionRecord = ProtectionRecord.single(packet,
                 connection::allocateDatagramForEncryption);
+        // close the connection ID managers; any in-flight connection ID changes should be ignored.
+        connection.localConnectionIdManager().close();
+        connection.peerConnectionIdManager().close();
         // while sending the packet containing the CONNECTION_CLOSE frame, the pushDatagram will
         // remap the QuicConnectionImpl in QuicEndpoint.
         connection.pushDatagram(protectionRecord);
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
index 2bc759a920a..0646026e28b 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java
@@ -65,6 +65,7 @@ final class PeerConnIdManager {
     private final QuicConnectionImpl connection;
     private final String logTag;
     private final boolean isClient;
+    private boolean closed; // when true, no more reset tokens are registered
 
     private enum State {
         INITIAL_PKT_NOT_RECEIVED_FROM_PEER,
@@ -267,6 +268,7 @@ void handshakeStatelessResetToken(final byte[] statelessResetToken) {
             if (handshakeConnId == null) {
                 throw new IllegalStateException("No handshake peer connection available");
             }
+            if (closed) return;
             // recreate the conn id with the stateless token
             this.peerConnectionIds.put(0L, new PeerConnectionId(handshakeConnId.asReadOnlyBuffer(),
                     statelessResetToken));
@@ -283,6 +285,10 @@ void handshakeStatelessResetToken(final byte[] statelessResetToken) {
     public List activeResetTokens() {
         lock.lock();
         try {
+            // this method is currently only used to remove a connection from the endpoint
+            // after the connection is closed.
+            // The below assert can be removed if the method is needed elsewhere.
+            assert closed;
             // we only support one active connection ID at the time
             PeerConnectionId cid = peerConnectionIds.get(activeConnIdSeq);
             byte[] statelessResetToken = null;
@@ -305,7 +311,7 @@ public List activeResetTokens() {
     QuicConnectionId getPeerConnId() {
         lock.lock();
         try {
-            if (activeConnIdSeq < largestReceivedRetirePriorTo) {
+            if (activeConnIdSeq < largestReceivedRetirePriorTo && !closed) {
                 // stop using the old connection ID
                 switchConnectionId();
             }
@@ -496,9 +502,11 @@ private void retirePriorTo(final long priorTo) {
             // connection ids. It does however store the peer-issued stateless reset token of a
             // peer connection id, so we let the endpoint know that the stateless reset token needs
             // to be forgotten since the corresponding peer connection id is being retired
-            final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken();
-            if (resetTokenToForget != null) {
-                this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget);
+            if (seqNumToRetire == activeConnIdSeq) {
+                final byte[] resetTokenToForget = entry.getValue().getStatelessResetToken();
+                if (resetTokenToForget != null) {
+                    this.connection.endpoint().forgetStatelessResetToken(resetTokenToForget);
+                }
             }
         }
         for (Iterator iterator = gaps.iterator(); iterator.hasNext(); ) {
@@ -540,4 +548,13 @@ public QuicFrame nextFrame(int remaining) {
             lock.unlock();
         }
     }
+
+    public void close() {
+        lock.lock();
+        try {
+            closed = true;
+        } finally {
+            lock.unlock();
+        }
+    }
 }
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
index 41b814a551c..b13d49ead7d 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java
@@ -1758,6 +1758,10 @@ LocalConnIdManager localConnectionIdManager() {
         return localConnIdManager;
     }
 
+    PeerConnIdManager peerConnectionIdManager() {
+        return peerConnIdManager;
+    }
+
     /**
      * {@return the local connection id}
      */
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
index ef342d4cb56..3dee814e1f1 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java
@@ -1532,12 +1532,16 @@ public void pushClosedDatagram(QuicConnectionImpl connection,
      */
     void removeConnection(final QuicPacketReceiver connection) {
         if (debug.on()) debug.log("removing connection " + connection);
-        // remove the connection completely
-        connection.connectionIds().forEach(connections::remove);
-        assert !connections.containsValue(connection) : connection;
         // remove references to this connection from the map which holds the peer issued
         // reset tokens
         dropPeerIssuedResetTokensFor(connection);
+        // remove the connection completely
+        connection.connectionIds().forEach(connections::remove);
+        assert !connections.containsValue(connection) : connection;
+        // Check that if there are no connections, there are no reset tokens either.
+        // This is safe because connections are added before reset tokens and removed after,
+        // except when we're closing the endpoint and don't bother with removing tokens.
+        assert peerIssuedResetTokens.isEmpty() || !connections.isEmpty() || closed : peerIssuedResetTokens;
     }
 
     /**
@@ -1587,7 +1591,6 @@ public void draining(final QuicConnectionImpl connection) {
         if (closed) return;
 
         final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
-        connection.localConnectionIdManager().close();
         DrainingConnection draining = new DrainingConnection(connection.connectionIds(),
                 connection.activeResetTokens(), idleTimeout);
         // we can ignore stateless reset in the draining state.
@@ -1626,7 +1629,6 @@ protected void closing(QuicConnectionImpl connection, ByteBuffer datagram) {
         closingDatagram.flip();
 
         final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
-        connection.localConnectionIdManager().close();
         var closingConnection = new ClosingConnection(connection.connectionIds(),
                 connection.activeResetTokens(), idleTimeout, datagram);
         remapPeerIssuedResetToken(closingConnection);
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
index 9e441cf7873..df8c229a000 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/TerminationCause.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -128,6 +128,9 @@ private static IOException toReportedCause(final Throwable original,
                     ? new IOException("connection terminated")
                     : new IOException(fallbackExceptionMsg);
         } else if (original instanceof QuicTransportException qte) {
+            if (qte.getCause() instanceof IOException ioe) {
+                return ioe;
+            }
             return new IOException(qte.getMessage());
         } else if (original instanceof IOException ioe) {
             return ioe;
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
index d0afd126bf6..4eb6d12fd38 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/DocSourcePositions.java
@@ -83,6 +83,7 @@ default long getStartPosition(CompilationUnitTree file, DocCommentTree comment,
      * @param comment the comment tree that encloses the tree for which the
      *                position is being sought
      * @param tree tree for which a position is sought
+     * @since 27
      */
     long getStartPosition(DocCommentTree comment, DocTree tree);
 
@@ -146,6 +147,7 @@ default long getEndPosition(CompilationUnitTree file, DocCommentTree comment, Do
      * @param comment the comment tree that encloses the tree for which the
      *                position is being sought
      * @param tree tree for which a position is sought
+     * @since 27
      */
     long getEndPosition(DocCommentTree comment, DocTree tree);
 
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
index 460f4f2a1ce..3ff6fafe58b 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SourcePositions.java
@@ -74,6 +74,7 @@ default long getStartPosition(CompilationUnitTree file, Tree tree) {
      * 

* * @param tree tree for which a position is sought + * @since 27 */ long getStartPosition(Tree tree); @@ -130,6 +131,7 @@ default long getEndPosition(CompilationUnitTree file, Tree tree) { *

* * @param tree tree for which a position is sought + * @since 27 */ long getEndPosition(Tree tree); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 8eba79c7480..452d15ed219 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -154,6 +154,14 @@ public void organizeTypeAnnotationsSignaturesForLocalVarType(final Env env1 = env; + while (env1 != null && !env1.tree.hasTag(Tag.CLASSDEF)) { + if (env1.tree instanceof JCLambda l) { + pos.currentLambda = l; + break; + } + env1 = env1.next; + } pos.scan(tree); } finally { log.useSource(oldSource); @@ -472,11 +480,12 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type, Assert.check(tc.position == pos); } - if (type.hasTag(TypeTag.ARRAY)) - return rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos); + Type ret; - if (type.hasTag(TypeTag.TYPEVAR)) { - return type.annotatedType(onlyTypeAnnotations); + if (type.hasTag(TypeTag.ARRAY)) { + ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos); + } else if (type.hasTag(TypeTag.TYPEVAR)) { + ret = type.annotatedType(onlyTypeAnnotations); } else if (type.getKind() == TypeKind.UNION) { // There is a TypeKind, but no TypeTag. UnionClassType ut = (UnionClassType) type; @@ -487,7 +496,7 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type, ListBuffer alternatives = new ListBuffer<>(); alternatives.add(res); alternatives.addAll(ut.alternatives_field.tail); - return new UnionClassType((ClassType) ut.getLub(), alternatives.toList()); + ret = new UnionClassType((ClassType) ut.getLub(), alternatives.toList()); } else { Type enclTy = type; Element enclEl = type.asElement(); @@ -567,10 +576,10 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type, pos.location = pos.location.appendList(depth.toList()); } - Type ret = typeWithAnnotations(type, enclTy, annotations); - typetree.type = ret; - return ret; + ret = typeWithAnnotations(type, enclTy, annotations); } + typetree.type = ret; + return ret; } /** diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java index 09b64efbdd0..1e875691537 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,9 +181,9 @@ private boolean updatePaths() throws IOException, DirectoryIteratorException { List added = new ArrayList<>(); Set current = new HashSet<>(); for (Path p : dirStream) { - if (!pathLookup.containsKey(p)) { - String s = p.toString(); - if (s.endsWith(".jfr")) { + String s = p.toString(); + if (s.endsWith(".jfr")) { + if (!pathLookup.containsKey(p)) { added.add(p); Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "New file found: " + p.toAbsolutePath()); } diff --git a/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java b/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java index 8ce7c03f62d..9ed1069155b 100644 --- a/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java +++ b/src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.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 @@ -117,11 +117,8 @@ public interface HotSpotDiagnosticMXBean extends PlatformManagedObject { * does not exist. * *

When the format is specified as {@link ThreadDumpFormat#JSON JSON}, the - * thread dump is generated in JavaScript Object Notation. - * threadDump.schema.json - * describes the thread dump format in draft - * - * JSON Schema Language version 2. + * thread dump is generated as JSON text in the + * JSON Thread Dump Format. * *

The thread dump will include output for all platform threads. It may * include output for some or all virtual threads. diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html new file mode 100644 index 00000000000..f7dd84d4f32 --- /dev/null +++ b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.html @@ -0,0 +1,283 @@ + + + + + JSON Thread Dump Format + + + +

JSON Thread Dump Format

+ +The JSON text is an object with a member named "threadDump" that is an object with the +following members: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
threadDump members
NameData typeOptionalDescription
formatVersion number no The format version. Its value is 2.
processId number no The process identifier (pid) of the VM that generated the thread dump.
time string no The timestamp, in ISO 8601 format, when the thread dump was generated.
runtimeVersion string no The {@linkplain java.lang.Runtime.Version runtime version} of the VM that + generated the thread dump.
threadContainers array no The array of thread "groupings". The first element is the root grouping/container + with name "<root>".
+ +

Each element in the "threadContainers" array is an object that represents a + grouping/container of threads with the following members: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
threadContainers members
NameData typeOptionalDescription
container string no The thread container name. The name is unique.
parent string or null no The thread container parent's name or {@code null} for the root container.
owner number or null no The {@linkplain java.lang.Thread#threadId() thread identifier} of the thread + that owns the thread container or {@code null} if no owner.
threads array no The array of threads in the thread grouping/container.
threadCount number yes The number of threads in the thread grouping/container. If the thread dump + includes all virtual threads then this count is the same as the number of + elements in the threads array. If all virtual threads are not included then + it may be larger than the number of elements in the threads array.
+ +

Each element in a "threads" array is an object with the following members: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
threads members
NameData typeOptionalDescription
tid number no The {@linkplain java.lang.Thread#threadId() thread identifier}.
time string no The timestamp, in ISO 8601 format, when the thread was sampled.
name string no The {@linkplain java.lang.Thread#getName() thread name}.
state string no The string representation of the {@linkplain java.lang.Thread#getState() thread state}.
virtual boolean yes {@code true} if the thread is a {@linkplain java.lang.Thread#isVirtual() virtual thread}.
carrier number yes The thread identifier of the carrier thread when this thread is a mounted + virtual thread.
stack array no The thread stack. The elements in the array are of type string with the string + representation of a {@linkplain java.lang.StackTraceElement stack trace element}. + If the thread stack has one or more elements, then the first element is the top + of the stack.
parkBlocker object yes The object responsible for the thread parking. Its members identify the + blocker object, and the exclusive owner thread if owned.
blockedOn string yes The {@linkplain java.util.Objects#toIdentityString(Object) identity string} of the + object that the thread is blocked on waiting to enter/re-enter a synchronization + method or block.
waitingOn string yes The {@linkplain java.util.Objects#toIdentityString(Object) identity string} + of the object that the thread is {@linkplain java.lang.Object#wait() waiting} + to be notified.
monitorsOwned array yes The objects for which a monitor is owned by the thread.
+ +

A "parkBlocker" object has the following members: + + + + + + + + + + + + + + + + + + + + + + + + +
parkBlocker members
NameData typeOptionalDescription
object string no The {@linkplain java.util.Objects#toIdentityString(Object) identity string} + of the {@linkplain java.util.concurrent.locks.LockSupport#park(Object) blocker + object} responsible for the thread parking.
owner number yes The thread identifier of the exclusive owner thread when the parkBlocker is + an {@link java.util.concurrent.locks.AbstractOwnableSynchronizer}.
+ +

Each element in a "monitorsOwned" array is an object with the following members:

+ + + + + + + + + + + + + + + + + + + + + + + + +
monitorsOwned members
NameData typeOptionalDescription
depth number no The stack depth at which the monitors are owned.
locks array no The elements of the array are of type string or null. An element of type string + has a value that is the {@linkplain java.util.Objects#toIdentityString(Object) + identity string} of the object for which the monitor is owned by the thread. + The element is of type null when the object has been eliminated.
+ + + diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json deleted file mode 100644 index 30161b0bb74..00000000000 --- a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "type": "object", - "properties": { - "threadDump": { - "type": "object", - "properties": { - "formatVersion": { - "type": "integer", - "description": "Format version (2)." - }, - "processId": { - "type": "integer", - "description": "The native process id of the Java virtual machine." - }, - "time": { - "type": "string", - "description": "The time in ISO 8601 format when the thread dump was generated." - }, - "runtimeVersion": { - "type": "string", - "description": "The runtime version, see java.lang.Runtime.Version" - }, - "threadContainers": { - "type": "array", - "description": "The array of thread containers (thread groupings).", - "items": [ - { - "type": "object", - "properties": { - "container": { - "type": "string", - "description": "The container name. The container name is unique." - }, - "parent": { - "type": [ "string", "null" ], - "description": "The parent container name or null for the root container." - }, - "owner": { - "type": [ "integer", "null" ], - "description": "The thread identifier of the owner thread or null if no owner." - }, - "threads": { - "type": "array", - "description": "The array of threads in the thread container.", - "items": [ - { - "type": "object", - "properties": { - "tid": { - "type": "integer", - "description": "The thread identifier." - }, - "time": { - "type": "string", - "description": "The time in ISO 8601 format that the thread was sampled." - }, - "name": { - "type": "string", - "description": "The thread name." - }, - "state": { - "type": "string", - "description": "The thread state (Thread::getState)." - }, - "virtual" : { - "type": "boolean", - "description": "true for a virtual thread." - }, - "parkBlocker": { - "type": "object", - "properties": { - "object": { - "type": "string", - "description": "The blocker object responsible for the thread parking." - } - }, - "owner": { - "type": "integer", - "description": "The thread identifier of the owner when the parkBlocker is an AbstractOwnableSynchronizer." - }, - "required": [ - "object" - ] - }, - "blockedOn": { - "type": "string", - "description": "The object that the thread is blocked on waiting to enter/re-enter a synchronization block/method." - }, - "waitingOn": { - "type": "string", - "description": "The object that the thread is waiting to be notified (Object.wait)." - }, - "stack": { - "type": "array", - "description": "The thread stack. The first element is the top of the stack.", - "items": [ - { - "type": "string", - "description": "A stack trace element (java.lang.StackTraceElement)." - } - ] - }, - "monitorsOwned": { - "type": "array", - "description": "The objects for which monitors are owned by the thread.", - "items": { - "type": "object", - "properties": { - "depth": { - "type": "integer", - "description": "The stack depth at which the monitors are owned." - }, - "locks": { - "type": "array", - "items": { - "type": [ "string", "null" ], - "description": "The object for which the monitor is owned by the thread, null if eliminated." - } - } - }, - "required": [ - "depth", - "locks" - ] - } - }, - "carrier": { - "type": "integer", - "description": "The thread identifier of the carrier thread if mounted." - } - }, - "required": [ - "tid", - "time", - "name", - "state", - "stack" - ] - } - ] - }, - "threadCount": { - "type": "integer", - "description": "The number of threads in the thread container." - } - }, - "required": [ - "container", - "parent", - "owner", - "threads" - ] - } - ] - } - }, - "required": [ - "formatVersion", - "processId", - "time", - "runtimeVersion", - "threadContainers" - ] - } - }, - "required": [ - "threadDump" - ] -} diff --git a/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java b/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java index a7935928cab..3b2451cb740 100644 --- a/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java +++ b/test/hotspot/jtreg/compiler/codecache/CheckLargePages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * 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 @@ * @bug 8304954 * @summary Code cache reservation should gracefully downgrade to using smaller pages if the code cache size is too small to host the requested page size. * @requires os.family == "linux" - * @requires vm.gc != "Z" + * @requires vm.flagless * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java b/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java new file mode 100644 index 00000000000..a0fb3525381 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/CPUFeaturesClearTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @library /test/lib / + * @modules java.base/jdk.internal.misc + * java.management + * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" + * @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 -Xbatch + * compiler.cpuflags.CPUFeaturesClearTest + */ + +package compiler.cpuflags; + +import java.util.List; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.cpuinfo.CPUInfo; +import static jdk.test.lib.cli.CommandLineOptionTest.*; + +public class CPUFeaturesClearTest { + private static List cpuFeaturesList; + public void runTestCases() throws Throwable { + if (Platform.isX64()) { + testX86Flags(); + } else if (Platform.isAArch64()) { + testAArch64Flags(); + } + } + + String[] generateArgs(String vmFlag) { + String[] args = {"-Xlog:os+cpu", "-XX:+UnlockDiagnosticVMOptions", vmFlag, "-version"}; + return args; + } + + public void testX86Flags() throws Throwable { + OutputAnalyzer outputAnalyzer; + String vmFlagsToTest[] = {"UseCLMUL", "UseAES", "UseFMA", "UseSHA"}; + String cpuFeatures[] = {"clmul", "aes", "fma", "sha"}; + for (int i = 0; i < vmFlagsToTest.length; i++) { + String vmFlag = vmFlagsToTest[i]; + String cpuFeature = cpuFeatures[i]; + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag(vmFlag, false))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* " + cpuFeatures[i] + ".*"); + } + if (isCpuFeatureSupported("sse4")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 3))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse4.*"); + } + if (isCpuFeatureSupported("sse3")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 2))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse3.*"); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* ssse3.*"); + } + if (isCpuFeatureSupported("sse2")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 1))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse2.*"); + } + if (isCpuFeatureSupported("sse")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSSE", 0))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sse.*"); + } + if (isCpuFeatureSupported("avx512f")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 2))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx512.*"); + } + if (isCpuFeatureSupported("avx2")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 1))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx2.*"); + } + if (isCpuFeatureSupported("avx")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseAVX", 0))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* avx.*"); + } + } + + public void testAArch64Flags() throws Throwable { + OutputAnalyzer outputAnalyzer; + String vmFlagsToTest[] = {"UseCRC32", "UseLSE", "UseAES"}; + String cpuFeatures[] = {"crc32", "lse", "aes"}; + for (int i = 0; i < vmFlagsToTest.length; i++) { + String vmFlag = vmFlagsToTest[i]; + String cpuFeature = cpuFeatures[i]; + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag(vmFlag, false))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* " + cpuFeatures[i] + ".*"); + } + + // Disabling UseSHA should clear all shaXXX cpu features + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareBooleanFlag("UseSHA", false))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha1.*"); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha256.*"); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha3.*"); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sha512.*"); + + if (isCpuFeatureSupported("sve2")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSVE", 1))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sve2.*"); + verifyOutput(null, new String[]{"sve2"}, null, outputAnalyzer); + } + if (isCpuFeatureSupported("sve")) { + outputAnalyzer = ProcessTools.executeTestJava(generateArgs(prepareNumericFlag("UseSVE", 0))); + outputAnalyzer.shouldNotMatch("[os,cpu] CPU: .* sve.*"); + verifyOutput(null, new String[]{"sve"}, null, outputAnalyzer); + } + } + + static boolean isCpuFeatureSupported(String feature) { + return cpuFeaturesList.contains(feature); + } + + public static void main(String args[]) throws Throwable { + cpuFeaturesList = CPUInfo.getFeatures(); + new CPUFeaturesClearTest().runTestCases(); + } +} diff --git a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java index 9542e48e54e..60185fa91ad 100644 --- a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java +++ b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.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 @@ -32,6 +32,7 @@ * @key stress randomness * @bug 8252219 8256535 8325478 * @requires vm.compiler2.enabled + * @requires vm.flagless * @summary Tests that using a stress option without -XX:StressSeed=N generates * and logs a random seed. * @library /test/lib / diff --git a/test/hotspot/jtreg/compiler/debug/TestStressCM.java b/test/hotspot/jtreg/compiler/debug/TestStressCM.java index 0fb624bc16f..a6065fa4d09 100644 --- a/test/hotspot/jtreg/compiler/debug/TestStressCM.java +++ b/test/hotspot/jtreg/compiler/debug/TestStressCM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ static String cmTrace(String stressOpt, int stressSeed) throws Exception { "-XX:CompileOnly=" + className + "::sum", "-XX:+TraceOptoPipelining", "-XX:+" + stressOpt, "-XX:StressSeed=" + stressSeed, className, "10"}; - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(procArgs); + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(procArgs); OutputAnalyzer out = new OutputAnalyzer(pb.start()); out.shouldHaveExitValue(0); // Extract the trace of our method (the last one after those of all diff --git a/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java b/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java index 89da61a5763..1e76887732f 100644 --- a/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java +++ b/test/hotspot/jtreg/compiler/debug/VerifyAdapterSharing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, 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 @@ -42,7 +42,7 @@ public static void main(String[] args) throws Exception { ProcessBuilder pb; OutputAnalyzer out; - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xcomp", "-XX:+IgnoreUnrecognizedVMOptions", + pb = ProcessTools.createTestJavaProcessBuilder("-Xcomp", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+VerifyAdapterSharing", "-version"); out = new OutputAnalyzer(pb.start()); out.shouldHaveExitValue(0); diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java new file mode 100644 index 00000000000..a2a0a1a05f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestArrayCopyEliminationUncRematerialization.java @@ -0,0 +1,666 @@ +/* + * 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 8370416 + * @key randomness + * @summary Ensure that rematerialization loads for a scalarized arraycopy destination use the correct control and memory state. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.escapeAnalysis; + +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.Random; +import java.util.stream.Collectors; + +import jdk.test.lib.Utils; +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.Template.ZeroArgs; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.Hooks; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.TestFrameworkClass; + + +public class TestArrayCopyEliminationUncRematerialization { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final String PACKAGE = "compiler.escapeAnalysis.templated"; + private static final String CLASS_NAME = "TestArrayCopyEliminationUncRematerializationGenerated"; + + // This generates a test containing test methods of the form + // static int testStore(int[] src, boolean flag) { + // int[] dst = new int[COPY_LEN]; + // System.arraycopy(src, 0, dst, 0, COPY_LEN); + // src[WRITE_IDX] = WRITE_VAL_I; // Pollute the element in the source array corresponding to the returned element in dst. + // if (flag) { // Compiles to unstable if trap when called exclusively with flag = false. + // dst[0] = (byte) 0x7f; + // } + // return dst[RETURN_IDX]; // Corresponds to src[WRITE_IDX] + // } + // for all primitive types except boolean and for different methods of polluting the source + // array and producing an unstable if trap. + // The templates below generate an IR test that validates that as many rematerialization loads + // as possible are placed in the uncommon path and that the returned result is in fact from + // the source array. + // Between different runs of the test, it will generate different sized arrays and indices + // to read from and store to (see TestConfig below). Further, this generates a variant of the + // test method, where the offset into src is provided in an argument. C2 cannot put any rematerialization + // loads in the uncommon path then, but it is useful for checking the correct result. + public static void main(String[] args) { + final CompileFramework comp = new CompileFramework(); + + comp.addJavaSourceCode(PACKAGE + "." + CLASS_NAME, generate(comp)); + + comp.compile(); + + // Ensure consistent results for the node counts in the arraycopy subtests. + comp.invoke(PACKAGE + "." + CLASS_NAME, "main", new Object[] { new String[] { } }); + } + + private record TestConfig(int srcSize, int copyLen, int copyIdx, int writeIdx, int returnIdx) { + static TestConfig make() { + int copyLen = RANDOM.nextInt(10, 64); // 64 is the default value for -XX:EliminateAllocationArraySizeLimit. + int srcSize = RANDOM.nextInt(copyLen + 20, 1000); + int copyIdx = RANDOM.nextInt(1, srcSize - copyLen); // The index we start arraycopying src from. + int returnIdx = RANDOM.nextInt(0, copyLen); // The index where dst returns from. Must correspond to writeIdx in src. + int writeIdx = copyIdx + returnIdx; // The index we write to in src. + + return new TestConfig(srcSize, copyLen, copyIdx, writeIdx, returnIdx); + } + + public TemplateToken constDefinitions() { + return Template.make(() -> scope( + Hooks.CLASS_HOOK.insert(scope( + String.format("private static final int SRC_SIZE = %d;\n", srcSize), + String.format("private static final int COPY_LEN = %d;\n", copyLen), + String.format("private static final int COPY_IDX = %d;\n", copyIdx), + String.format("private static final int WRITE_IDX = %d;\n", writeIdx), + String.format("private static final int RETURN_IDX = %d;\n", returnIdx) + )))).asToken(); + } + + public int copyEnd() { + return copyIdx + copyLen; + } + } + + private static String generate(CompileFramework comp) { + TestConfig config = TestConfig.make(); + + final List tests = new ArrayList<>(); + tests.add(config.constDefinitions()); + tests.add(PrimitiveType.generateLibraryRNG()); + + // Generate all testcases for all primitive types except boolean. + tests.addAll(CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES + .stream() + .map(pty -> new TestsPerType(pty).generate(config)) + .collect(Collectors.toList())); + + final Set imports = Set.of("java.lang.foreign.MemorySegment", + "java.lang.foreign.ValueLayout", + "java.lang.invoke.MethodHandles", + "java.lang.invoke.VarHandle", + "java.util.Arrays", + "java.util.Random", + "jdk.test.lib.Asserts", + "jdk.test.lib.Utils", + "compiler.lib.generators.*"); + + return TestFrameworkClass.render(PACKAGE, CLASS_NAME, imports, comp.getEscapedClassPathOfCompiledClasses(), tests); + } + + record TestsPerType(PrimitiveType pty) { + private record TestTemplates(ZeroArgs store, ZeroArgs trap, ZeroArgs prelude) { + TestTemplates(ZeroArgs store, ZeroArgs trap) { + this(store, trap, Template.make(() -> scope(""))); + } + } + + TemplateToken generate(TestConfig config) { + final String srcArray = "src" + pty.abbrev(); + final String handle = pty.name().toUpperCase() + "_ARR"; + + var runTestConst = Template.make("testName", (String testName) -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("srcField", srcArray), + """ + @Run(test = "test#{testName}") + static void run#{testName}(RunInfo info) { + Arrays.fill(#{srcField}, SRC_VAL_#{typeAbbrev}); + #type res = test#{testName}(#{srcField}, #{srcField}, info.isWarmUp()); + Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong result from " + info.getTest().getName() + " with flag " + info.isWarmUp()); + } + """ + )); + + var runTestIdx = Template.make("testName", (String testName) -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("srcField", srcArray), + """ + @Run(test = "test#{testName}") + static void run#{testName}(RunInfo info) { + Arrays.fill(#{srcField}, SRC_VAL_#{typeAbbrev}); + #type res = test#{testName}(#{srcField}, COPY_IDX, info.isWarmUp()); + Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong result from " + info.getTest().getName() + " with flag " + info.isWarmUp()); + } + """ + )); + + var testMethodConst = Template.make("testName", "tmp", (String TestName, TestTemplates templates) -> scope( + let("type", pty), + """ + static #type test#{testName}(#type[] src, #type[] alias, boolean flag) { + """, + templates.prelude.asToken(), + """ + #type[] dst = new #type[COPY_LEN]; + System.arraycopy(src, COPY_IDX, dst, 0, COPY_LEN); + """, + templates.store.asToken(), + templates.trap.asToken(), + """ + return dst[RETURN_IDX]; + } + """ + )); + + var testMethodIdx = Template.make("testName", "tmp", (String TestName, TestTemplates templates) -> scope( + let("type", pty), + """ + static #type test#{testName}(#type[] src, int idx, boolean flag) { + """, + templates.prelude.asToken(), + """ + #type[] dst = new #type[COPY_LEN]; + System.arraycopy(src, idx, dst, 0, COPY_LEN); + """, + templates.store.asToken(), + templates.trap.asToken(), + """ + return dst[RETURN_IDX]; + } + """ + )); + + var testMethodClone = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope( + let("type", pty), + """ + static #type test#{testName}(#type[] realSrc, #type[] alias, boolean flag) { + """, + templates.prelude.asToken(), + """ + // Set up a src array with statically known length, so scalar replacement works. + #type[] src = new #type[COPY_LEN]; + System.arraycopy(realSrc, COPY_IDX, src, 0, COPY_LEN); + + // Clone the src array into a dst array to get the clonebasic variant of the ArraycopyNode. + #type[] dst = src.clone(); + """, + templates.store.asToken(), + templates.trap.asToken(), + """ + return dst[RETURN_IDX]; + } + """ + )); + + // For methods with a constant offset into src, validate that only the necessary rematerialization nodes are + // in the common path. + var testCaseConst = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope( + let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()), + runTestConst.asToken(testName), + """ + @Test + @IR(counts = { IRNode.LOAD_#{typeAbbrev}, "=#{loadCount}" }, + applyIf = { "TieredCompilation", "true"}) + """, + testMethodConst.asToken(testName, templates) + )); + + var testCaseConstPlusOne = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope( + let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()), + runTestConst.asToken(testName), + let("countPlusOne", loadCount + 1), + """ + @Test + @IR(counts = { IRNode.LOAD_#{typeAbbrev}, ">=#{loadCount}", + IRNode.LOAD_#{typeAbbrev}, "<=#{countPlusOne}" }, + applyIf = { "TieredCompilation", "true"}) + """, + testMethodConst.asToken(testName, templates) + )); + + // Some test cases can not be reliably verified due to varying numbers of loads generated from run to run. + var testCaseConstNoVerify = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope( + runTestConst.asToken(testName), + """ + @Test + """, + testMethodConst.asToken(testName, templates) + )); + + // For methods with a parametrized offset into src, only validate the correctness of the return value. + var testCaseIdx = Template.make("testName", "tmp", (String testName, TestTemplates templates) -> scope( + runTestIdx.asToken(testName), + """ + @Test + """, + testMethodIdx.asToken(testName, templates) + )); + + // Generates tests with the clonebasic variant of the ArraycopyNode. + var testCaseClone = Template.make("testName", "loadCount", "tmp", (String testName, Integer loadCount, TestTemplates templates) -> scope( + let("typeAbbrev", pty.abbrev().equals("C") ? "US" : pty.abbrev()), + runTestConst.asToken(testName), + """ + @Test + @IR(counts = { IRNode.LOAD_#{typeAbbrev}, "=#{loadCount}" }, + applyIf = { "TieredCompilation", "true"}) + """, + testMethodClone.asToken(testName, templates) + )); + + var storeConst = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + """ + src[WRITE_IDX] = WRITE_VAL_#{typeAbbrev}; + """ + )); + + var storeIdx = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + """ + src[idx + RETURN_IDX] = WRITE_VAL_#{typeAbbrev}; + """ + )); + + var storeClone = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + """ + src[RETURN_IDX] = WRITE_VAL_#{typeAbbrev}; + """ + )); + + var storeAlias = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + """ + alias[WRITE_IDX] = WRITE_VAL_#{typeAbbrev}; + """ + )); + + var unstableTrap = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + """ + if (flag) { + src[0] = WRITE_VAL_#{typeAbbrev}; + } + """ + )); + + // This generates test with a store to the returned src element and a simple unstable if trap. + // Only one rematerialization load is in the common path. + var testStore = Template.make(() -> { + final String testName = "Store" + pty.abbrev(); + return scope( + testCaseConst.asToken("Const" + testName, 2 * config.copyLen - 1, new TestTemplates(storeConst, unstableTrap)), + testCaseIdx.asToken("Idx" + testName, new TestTemplates(storeIdx, unstableTrap)), + testCaseClone.asToken("Clone" + testName, config.copyLen, new TestTemplates(storeClone, unstableTrap)), + testCaseConst.asToken("Alias" + testName, 3 * config.copyLen - 2, new TestTemplates(storeAlias, unstableTrap)) + ); + }); + + // This generates tests with multiple interfering stores forcing elements to be put into the common path. + var testMultiStore = Template.make(() -> { + final String testName = "MultiStore" + pty.abbrev(); + final int numStores = RANDOM.nextInt(1, config.copyLen - 1); + // Get numStores different store locations. + final Set storeIdxs = new HashSet<>(); + storeIdxs.add(config.returnIdx); // Always store at the WRITE_IDX to potentially trigger the bug. + while (storeIdxs.size() < numStores) { + storeIdxs.add(RANDOM.nextInt(0, config.copyLen)); + } + var multiStoresConst = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + storeIdxs.stream() + .map(idx -> scope(let("idx", idx), "src[COPY_IDX + #idx] = WRITE_VAL_#{typeAbbrev};\n")) + .toList() + )); + var multiStoresIdx = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + storeIdxs.stream() + .map(idx -> scope(let("idx", idx), "src[idx + #idx] = WRITE_VAL_#{typeAbbrev};\n")) + .toList() + )); + var multiStoresClone = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + storeIdxs.stream() + .map(idx -> scope(let("idx", idx), "src[#idx] = WRITE_VAL_#{typeAbbrev};\n")) + .toList() + )); + var multiStoresAlias = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + storeIdxs.stream() + .map(idx -> scope(let("idx", idx), "alias[COPY_IDX + #idx] = WRITE_VAL_#{typeAbbrev};\n")) + .toList() + )); + return scope( + // Sometimes we get one more load depending on the position of the range checks of the different stores. + testCaseConstPlusOne.asToken("Const" + testName, 2 * config.copyLen - numStores, new TestTemplates(multiStoresConst, unstableTrap)), + testCaseIdx.asToken("Idx" + testName, new TestTemplates(multiStoresIdx, unstableTrap)), + testCaseClone.asToken("Clone" + testName, config.copyLen, new TestTemplates(multiStoresClone, unstableTrap)), + testCaseConstPlusOne.asToken("Alias" + testName, 3 * config.copyLen - 2 * numStores, new TestTemplates(multiStoresAlias, unstableTrap)) + ); + }); + + // This generates test with a store to the returned src element and the unstable if trap inside a loop. + // Only one rematerialization load is in the common path. + var testStoreTrapLoop = Template.make(() -> { + final String testName = "StoreTrapLoop" + pty.abbrev(); + var trapTemplate = Template.make(() -> scope( + """ + for (int i = 0; i < 1234; i++) { + """, + unstableTrap.asToken(), + """ + } + """ + )); + return scope( + testCaseConst.asToken("Const" + testName, 2 * config.copyLen - 1, new TestTemplates(storeConst, trapTemplate)), + testCaseIdx.asToken("Idx" + testName, new TestTemplates(storeIdx, trapTemplate)), + testCaseConst.asToken("Alias" + testName, 2 * config.copyLen - 1, new TestTemplates(storeAlias, trapTemplate)) + ); + }); + + // This generates tests with atomic operations (LoadStores) as polluting store. + // Only one rematerialization load is in the common path. + var testAtomics = Template.make(() -> { + var getAndSetStoreConst = Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("handle", handle), + """ + #{handle}.getAndSet(src, WRITE_IDX, (#type) WRITE_VAL_#{typeAbbrev}); + """ + )); + var getAndSetStoreIdx = Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("handle", handle), + """ + #{handle}.getAndSet(src, idx + RETURN_IDX, (#type) WRITE_VAL_#{typeAbbrev}); + """ + )); + var casStoreConst = Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("handle", handle), + """ + #{handle}.compareAndSet(src, WRITE_IDX, (#type) SRC_VAL_#{typeAbbrev}, (#type) WRITE_VAL_#{typeAbbrev}); + """ + )); + var casStoreIdx = Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("handle", handle), + """ + #{handle}.compareAndSet(src, idx + RETURN_IDX, (#type) SRC_VAL_#{typeAbbrev}, (#type) WRITE_VAL_#{typeAbbrev}); + """ + )); + return scope(let("type", pty), + let("handle", handle), + Hooks.CLASS_HOOK.insert(scope( + """ + private static final VarHandle #handle = MethodHandles.arrayElementVarHandle(#type[].class); + """ + )), + // We cannot look through MemBars emitted by the atomic operations, so all rematerialization loads are + // commoned up in the common path. + pty.abbrev().equals("S") || pty.abbrev().equals("B") || pty.abbrev().equals("C") ? + testCaseConstNoVerify.asToken("ConstGetAndSet" + pty.abbrev(), new TestTemplates(getAndSetStoreConst, unstableTrap)) : + testCaseConst.asToken("ConstGetAndSet" + pty.abbrev(), config.copyLen, new TestTemplates(getAndSetStoreConst, unstableTrap)), + testCaseIdx.asToken("IdxGetAndSet" + pty.abbrev(), new TestTemplates(getAndSetStoreIdx, unstableTrap)), + testCaseConst.asToken("ConstCompareAndSet" + pty.abbrev(), config.copyLen, new TestTemplates(casStoreConst, unstableTrap)), + testCaseIdx.asToken("IdxCompareAndSet" + pty.abbrev(), new TestTemplates(casStoreIdx, unstableTrap)) + ); + }); + + var testMemorySegments = Template.make(() -> { + final String layout = String.format("JAVA_%s", pty.toString().toUpperCase()); + var memorySegmentCreation = Template.make(() -> scope( + """ + MemorySegment srcMS = MemorySegment.ofArray(src); + """ + )); + var memorySegmentCreationAlias = Template.make(() -> scope( + """ + MemorySegment srcMS = MemorySegment.ofArray(alias); + """ + )); + // Just write using a memory segment + var memorySegmentStoreConst = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + let("layout", layout), + """ + srcMS.setAtIndex(ValueLayout.#{layout}, WRITE_IDX, WRITE_VAL_#{typeAbbrev}); + """ + )); + var memorySegmentStoreIdx = Template.make(() -> scope( + let("typeAbbrev", pty.abbrev()), + let("layout", layout), + """ + srcMS.setAtIndex(ValueLayout.#{layout}, RETURN_IDX + idx, WRITE_VAL_#{typeAbbrev}); + """ + )); + // Write a single byte somewhere within the returned value. + var memorySegmentStoreSmallConst = Template.make(() -> scope( + let("offset", RANDOM.nextInt(pty.byteSize())), + let("byteSize", pty.byteSize()), + """ + srcMS.set(ValueLayout.JAVA_BYTE, WRITE_IDX * #byteSize - #offset, (byte)-1); + """ + )); + var memorySegmentStoreSmallIdx = Template.make(() -> scope( + let("offset", RANDOM.nextInt(pty.byteSize())), + let("byteSize", pty.byteSize()), + """ + srcMS.set(ValueLayout.JAVA_BYTE, (RETURN_IDX + idx) * #byteSize - #offset, (byte)-1); + """ + )); + // Write 8 bytes overlapping multiple array elements. + int offsetMut = RANDOM.nextInt(pty.byteSize()); + if (config.writeIdx * pty.byteSize() - offsetMut + 8 >= config.srcSize * pty.byteSize()) { + // This will not fit. Adjust offset. + offsetMut = (config.writeIdx * pty.byteSize() + 8) - (config.srcSize * pty.byteSize()); + } + final int offset = offsetMut; + var memorySegmentStoreOverlappingConst = Template.make(() -> scope( + let("offset", offset), + let("byteSize", pty.byteSize()), + """ + srcMS.set(ValueLayout.JAVA_LONG_UNALIGNED, WRITE_IDX * #byteSize - #offset, -1); + """ + )); + var memorySegmentStoreOverlappingIdx = Template.make(() -> scope( + let("offset", offset), + let("byteSize", pty.byteSize()), + """ + srcMS.set(ValueLayout.JAVA_LONG_UNALIGNED, (RETURN_IDX + idx) * #byteSize - #offset, -1); + """ + )); + return scope( + let("type", pty), + // The number of loads differs run to run (probably due to the amount of inlining going on), so + // we do not verify the number of loads in the uncommon path, even though only the polluted loads + // end up in the common path for all const cases. + testCaseConstNoVerify.asToken("MemorySegmentStoreConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreConst, unstableTrap, memorySegmentCreation)), + testCaseIdx.asToken("MemorySegmentStoreIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreIdx, unstableTrap, memorySegmentCreation)), + testCaseConstNoVerify.asToken("MemorySegmentStoreAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreConst, unstableTrap, memorySegmentCreationAlias)), + testCaseConstNoVerify.asToken("MemorySegmentStoreSmallConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallConst, unstableTrap, memorySegmentCreation)), + testCaseIdx.asToken("MemorySegmentStoreSmallIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallIdx, unstableTrap, memorySegmentCreation)), + testCaseConstNoVerify.asToken("MemorySegmentStoreSmallAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreSmallConst, unstableTrap, memorySegmentCreationAlias)), + testCaseConstNoVerify.asToken("MemorySegmentStoreOverlappingConst" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingConst, unstableTrap, memorySegmentCreation)), + testCaseIdx.asToken("MemorySegmentStoreOverlappingIdx" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingIdx, unstableTrap, memorySegmentCreation)), + testCaseConstNoVerify.asToken("MemorySegmentStoreOverlappingAlias" + pty.abbrev(), new TestTemplates(memorySegmentStoreOverlappingConst, unstableTrap, memorySegmentCreationAlias)) + ); + }); + + // C2 is not able to put any rematerialization load in the uncommon path for this test. + // Thus, we do not check the number of loads. + var testSwitch = Template.make(() -> { + final String testName = "Switch" + pty.abbrev(); + return scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("testName", testName), + let("src", srcArray), + """ + @Run(test = "test#testName") + @Warmup(10000) + static void run#testName(RunInfo info) { + Arrays.fill(#src, SRC_VAL_#{typeAbbrev}); + #type res = test#testName(#src); + Asserts.assertEQ((#type) SRC_VAL_#{typeAbbrev}, res, "Wrong Result from " + info.getTest().getName()); + } + + @Test + static #type test#testName(#type[] src) { + #type[] dst = new #type[COPY_LEN]; + System.arraycopy(src, COPY_IDX, dst, 0, COPY_LEN); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + switch (i) { + case -1 -> { /*nop*/ } + case 0 -> src[WRITE_IDX] = WRITE_VAL_#{typeAbbrev}; + } + } + } + + return dst[1]; + } + """ + ); + }); + + // This generates tests where the polluting store to src is an arraycopy. + // In this case, the rematerialization loads for all elements that are written to + // have to be in the common path. + var testArraycopy = Template.make(() -> { + final String testName = "Arraycopy" + pty.abbrev(); + final int arraycopyLen = RANDOM.nextInt(1, Math.min(config.copyLen, config.srcSize - config.writeIdx)); + final int otherLen = RANDOM.nextInt(arraycopyLen + 1, arraycopyLen + 10); + final int copyOtherIdx = RANDOM.nextInt(0, otherLen - arraycopyLen); + var arraycopyStoreConst = Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("arraycopyLen", arraycopyLen), + let("other", "other" + pty.abbrev()), + let("otherLen", otherLen), + let("copyOtherIdx", copyOtherIdx), + Hooks.CLASS_HOOK.insert(scope( + """ + private static final #type[] #other = new #type[#otherLen]; + static { Arrays.fill(#other, WRITE_VAL_#{typeAbbrev}); } + """ + )), + """ + System.arraycopy(#other, #copyOtherIdx, src, WRITE_IDX, #arraycopyLen); + """ + )); + var arraycopyStoreIdx = Template.make(() -> scope( + let("type", pty), + let("arraycopyLen", arraycopyLen), + let("other", "other" + pty.abbrev()), + let("copyOtherIdx", copyOtherIdx), + """ + System.arraycopy(#other, #copyOtherIdx, src, idx + RETURN_IDX, #arraycopyLen); + """ + )); + var arraycopyStoreAlias = Template.make(() -> scope( + let("type", pty), + let("arraycopyLen", arraycopyLen), + let("other", "other" + pty.abbrev()), + let("copyOtherIdx", copyOtherIdx), + """ + System.arraycopy(#other, #copyOtherIdx, alias, WRITE_IDX, #arraycopyLen); + """ + )); + // Unfortunately, it is not possible to validate the placement of rematerialization nodes because + // the number of uncomon traps is sensitive to changes in the profile, which leads to a bimodal count + // of load nodes. + return scope( + testCaseConstNoVerify.asToken("Const" + testName, new TestTemplates(arraycopyStoreConst, unstableTrap)), + testCaseIdx.asToken("Idx" + testName, new TestTemplates(arraycopyStoreIdx, unstableTrap)), + testCaseConstNoVerify.asToken("Alias" + testName, new TestTemplates(arraycopyStoreAlias, unstableTrap)) + ); + }); + + return Template.make(() -> scope( + let("type", pty), + let("typeAbbrev", pty.abbrev()), + let("writeVal", pty.con()), + let("rng", pty.callLibraryRNG()), + let("src", srcArray), + Hooks.CLASS_HOOK.insert(scope( + """ + private static final #type SRC_VAL_#{typeAbbrev}; + private static final #type WRITE_VAL_#{typeAbbrev} = #writeVal; + private static final #type[] #src = new #type[SRC_SIZE]; + + static { + #type srcVal; + do { + srcVal = #rng; + } while (srcVal == WRITE_VAL_#{typeAbbrev}); + SRC_VAL_#{typeAbbrev} = srcVal; + } + """ + )), + List.of(testStore, + testMultiStore, + testStoreTrapLoop, + testAtomics, + testMemorySegments, + testSwitch, + testArraycopy) + .stream() + .map(t -> t.asToken()) + .collect(Collectors.toList()) + )).asToken(); + } + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java new file mode 100644 index 00000000000..9be192d1f55 --- /dev/null +++ b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java @@ -0,0 +1,127 @@ +/* + * 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.exceptions; + +import compiler.lib.ir_framework.CompLevel; +import compiler.lib.ir_framework.Run; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import jdk.test.lib.Asserts; +import test.java.lang.invoke.lib.InstructionHelper; + +/** + * @test + * @bug 8350208 + * @summary Safepoints added during the processing of exception handlers need correct stack state + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @build test.java.lang.invoke.lib.InstructionHelper + * + * @run main/othervm ${test.main.class} + */ +public class TestDebugDuringExceptionCatching { + + public static class V { + int v; + } + + static final int ITERATIONS = 100; + static final RuntimeException EXCEPTION = new RuntimeException(); + + /** + * Construct something that looks like this: + *
{@code
+     * int snippet(V v) {
+     *     int i = 0;
+     *     LoopHead: {
+     *         if (i >= 100) {
+     *             goto LoopEnd;
+     *         }
+     *         i++;
+     *         try {
+     *             v.v = 1;
+     *         } catch (Throwable) {
+     *             // Not really, the LoopHead is the exception Handler
+     *             goto LoopHead;
+     *         }
+     *     }
+     *     LoopEnd:
+     *     return i;
+     * }
+     * }
+ */ + static final MethodHandle SNIPPET_HANDLE; + static final ClassDesc CLASS_DESC = TestDebugDuringExceptionCatching.class.describeConstable().get(); + static { + SNIPPET_HANDLE = InstructionHelper.buildMethodHandle(MethodHandles.lookup(), + "snippet", + MethodType.methodType(int.class, V.class), + CODE -> { + Label loopHead = CODE.newLabel(); + Label loopEnd = CODE.newLabel(); + Label tryStart = CODE.newLabel(); + Label tryEnd = CODE.newLabel(); + CODE. + iconst_0(). + istore(1). + // The loop head should have a RuntimeException as the sole element on the stack + getstatic(CLASS_DESC, "EXCEPTION", RuntimeException.class.describeConstable().get()). + labelBinding(loopHead). + pop(). + iload(1). + ldc(ITERATIONS). + if_icmpge(loopEnd). + iinc(1, 1). + aload(0). + iconst_1(). + labelBinding(tryStart). + putfield(V.class.describeConstable().get(), "v", int.class.describeConstable().get()). + labelBinding(tryEnd). + // The stack is empty here + labelBinding(loopEnd). + iload(1). + ireturn(); + CODE.exceptionCatchAll(tryStart, tryEnd, loopHead); + }); + } + + @Test(compLevel = CompLevel.C2) // see JDK-8381786 + private static int testBackwardHandler(V v) throws Throwable { + return (int) SNIPPET_HANDLE.invokeExact(v); + } + + @Run(test = "testBackwardHandler") + public void run() throws Throwable { + Asserts.assertEQ(ITERATIONS, testBackwardHandler(null)); + } + + public static void main(String[] args) { + TestFramework.run(); + } +} diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 33be24a0367..dfb2ec1405c 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -30,7 +30,7 @@ * @modules jdk.incubator.vector * @library /test/lib / * @compile ../lib/verify/Verify.java - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer + * @run driver compiler.igvn.ExpressionFuzzer */ package compiler.igvn; @@ -73,6 +73,10 @@ // - Some basic IR tests to ensure that the constraints / checksum mechanics work. // We may even have to add some IGVN optimizations to be able to better observe things right. // - Lower the CompileTaskTimeout, if possible. It is chosen conservatively (rather high) for now. +// - I also had to exclude the compilation of the following method. It would lead to compilation +// timeouts and even compilation memory limit reached. It is a really large method, so I'm not +// sure if that is to be expected, or if we could still improve the situation. +// compiler.lib.template_framework.library.Operations::generateVectorOperations public class ExpressionFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); @@ -93,7 +97,8 @@ public static void main(String[] args) { comp.invoke("compiler.igvn.templated.ExpressionFuzzerInnerTest", "main", new Object[] {new String[] { "--add-modules=jdk.incubator.vector", "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED", - "--add-opens", "java.base/java.lang=ALL-UNNAMED" + "--add-opens", "java.base/java.lang=ALL-UNNAMED", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:CompileTaskTimeout=10000" }}); } diff --git a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java index bfb617a0a3a..1c2d07d3255 100644 --- a/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java +++ b/test/hotspot/jtreg/compiler/jvmci/TestUncaughtErrorInCompileMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * which is only read in a debug VM. * @requires vm.jvmci * @requires vm.debug + * @requires vm.flagless * @library /test/lib / * @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot * jdk.internal.vm.ci/jdk.vm.ci.code diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java index b461e3e857f..33eba66cd8c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/CodeGenerationDataNameType.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 @@ -132,6 +132,14 @@ public interface CodeGenerationDataNameType extends DataName.Type { longs() ); + /** + * List of {@link PrimitiveType}s (int, long). + */ + List INT_LONG_TYPES = List.of( + ints(), + longs() + ); + /** * List of all subword {@link PrimitiveType}s (byte, char, short). */ @@ -176,4 +184,72 @@ public interface CodeGenerationDataNameType extends DataName.Type { booleans(), float16() ); + + List VECTOR_BYTE_VECTOR_TYPES = List.of( + VectorType.BYTE_64, + VectorType.BYTE_128, + VectorType.BYTE_256, + VectorType.BYTE_512 + ); + + List VECTOR_SHORT_VECTOR_TYPES = List.of( + VectorType.SHORT_64, + VectorType.SHORT_128, + VectorType.SHORT_256, + VectorType.SHORT_512 + ); + + List VECTOR_INT_VECTOR_TYPES = List.of( + VectorType.INT_64, + VectorType.INT_128, + VectorType.INT_256, + VectorType.INT_512 + ); + + List VECTOR_LONG_VECTOR_TYPES = List.of( + VectorType.LONG_64, + VectorType.LONG_128, + VectorType.LONG_256, + VectorType.LONG_512 + ); + + List VECTOR_FLOAT_VECTOR_TYPES = List.of( + VectorType.FLOAT_64, + VectorType.FLOAT_128, + VectorType.FLOAT_256, + VectorType.FLOAT_512 + ); + + List VECTOR_DOUBLE_VECTOR_TYPES = List.of( + VectorType.DOUBLE_64, + VectorType.DOUBLE_128, + VectorType.DOUBLE_256, + VectorType.DOUBLE_512 + ); + + List VECTOR_VECTOR_TYPES = Utils.concat( + VECTOR_BYTE_VECTOR_TYPES, + VECTOR_SHORT_VECTOR_TYPES, + VECTOR_INT_VECTOR_TYPES, + VECTOR_LONG_VECTOR_TYPES, + VECTOR_FLOAT_VECTOR_TYPES, + VECTOR_DOUBLE_VECTOR_TYPES + ); + + List VECTOR_MASK_TYPES = + VECTOR_VECTOR_TYPES.stream().map(t -> t.maskType).toList(); + + List VECTOR_SHUFFLE_TYPES = + VECTOR_VECTOR_TYPES.stream().map(t -> t.shuffleType).toList(); + + List VECTOR_TYPES = Utils.concat( + VECTOR_VECTOR_TYPES, + VECTOR_MASK_TYPES, + VECTOR_SHUFFLE_TYPES + ); + + List ALL_TYPES = Utils.concat( + SCALAR_NUMERIC_TYPES, + VECTOR_TYPES + ); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 37ad4debde6..f3e7b6ed0d3 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.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 @@ -332,6 +332,72 @@ public static Expression make(CodeGenerationDataNameType returnType, return new Expression(returnType, List.of(t0, t1, t2, t3), List.of(s0, s1, s2, s3, s4), info); } + /** + * Creates a new Expression with 5 arguments. + * + * @param returnType The return type of the {@link Expression}. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The fourth string, to be placed before {@code t3}. + * @param t3 The type of the fourth argument. + * @param s4 The fifth string, to be placed before {@code t4}. + * @param t4 The type of the fifth argument. + * @param s5 The last string, finishing the {@link Expression}. + * @return the new {@link Expression}. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3, + CodeGenerationDataNameType t3, + String s4, + CodeGenerationDataNameType t4, + String s5) { + return make(returnType, s0, t0, s1, t1, s2, t2, s3, t3, s4, t4, s5, new Info()); + } + + /** + * Creates a new Expression with 5 arguments. + * + * @param returnType The return type of the {@link Expression}. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The fourth string, to be placed before {@code t3}. + * @param t3 The type of the fourth argument. + * @param s4 The fifth string, to be placed before {@code t4}. + * @param t4 The type of the fifth argument. + * @param s5 The last string, finishing the {@link Expression}. + * @param info Additional information about the {@link Expression}. + * @return the new {@link Expression}. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3, + CodeGenerationDataNameType t3, + String s4, + CodeGenerationDataNameType t4, + String s5, + Info info) { + return new Expression(returnType, List.of(t0, t1, t2, t3, t4), List.of(s0, s1, s2, s3, s4, s5), info); + } + /** * Creates a {@link TemplateToken} for the use in a {@link Template} by applying the * {@code arguments} to the {@link Expression}. It is the users responsibility to diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 2ea251cc5e5..4c598506e70 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -24,11 +24,8 @@ package compiler.lib.template_framework.library; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static compiler.lib.template_framework.library.PrimitiveType.BYTES; import static compiler.lib.template_framework.library.PrimitiveType.SHORTS; @@ -39,6 +36,10 @@ import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; import static compiler.lib.template_framework.library.Float16Type.FLOAT16; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INTEGRAL_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.FLOATING_TYPES; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.INT_LONG_TYPES; /** * This class provides various lists of {@link Expression}s, that represent Java operators or library @@ -51,27 +52,8 @@ private Operations() {} private static Expression.Info WITH_ARITHMETIC_EXCEPTION = new Expression.Info().withExceptions(Set.of("ArithmeticException")); private static Expression.Info WITH_NONDETERMINISTIC_RESULT = new Expression.Info().withNondeterministicResult(); - - - /** - * Provides a lits of operations on {@link PrimitiveType}s, such as arithmetic, logical, - * and cast operations. - */ - public static final List PRIMITIVE_OPERATIONS = generatePrimitiveOperations(); - - public static final List FLOAT16_OPERATIONS = generateFloat16Operations(); - - public static final List SCALAR_NUMERIC_OPERATIONS = concat( - PRIMITIVE_OPERATIONS, - FLOAT16_OPERATIONS - ); - - @SafeVarargs - private static List concat(List... lists) { - return Arrays.stream(lists) - .flatMap(List::stream) - .collect(Collectors.toList()); - } + private static Expression.Info WITH_ILLEGAL_ARGUMENT_EXCEPTION = new Expression.Info().withExceptions(Set.of("IllegalArgumentException")); + private static Expression.Info WITH_OUT_OF_BOUNDS_EXCEPTION = new Expression.Info().withExceptions(Set.of("IndexOutOfBoundsException")); private static void addComparisonOperations(List ops, String operatorName, CodeGenerationDataNameType type) { for (String mask : List.of("==", "!=", "<", ">", "<=", ">=")) { @@ -332,4 +314,522 @@ private static List generateFloat16Operations() { // Make sure the list is not modifiable. return List.copyOf(ops); } + + private enum VOPType { + UNARY, + BINARY, + ASSOCIATIVE, // Binary and associative - safe for reductions of any type + INTEGRAL_ASSOCIATIVE, // Binary - but only safe for integral reductions + TERNARY + } + private record VOP(String name, VOPType type, List elementTypes, boolean isDeterministic) { + VOP(String name, VOPType type, List elementTypes) { + this(name, type, elementTypes, true); + } + } + + // TODO: consider enforcing precision instead of just blanket non-determinism + // We could annotate the exact ulp, and so if some test can use it: great + // But if a test is just interested in determinism, they are still + // non-deterministic. + private static final List VECTOR_OPS = List.of( + new VOP("ABS", VOPType.UNARY, PRIMITIVE_TYPES), + new VOP("ACOS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("ADD", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("AND", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("AND_NOT", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("ASHR", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("ASIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("ATAN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("ATAN2", VOPType.BINARY, FLOATING_TYPES, false), // 2 ulp + new VOP("BIT_COUNT", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("BITWISE_BLEND", VOPType.TERNARY, INTEGRAL_TYPES), + new VOP("CBRT", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("COMPRESS_BITS", VOPType.BINARY, INT_LONG_TYPES), + new VOP("COS", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("COSH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp + new VOP("DIV", VOPType.BINARY, FLOATING_TYPES), + new VOP("EXP", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("EXPAND_BITS", VOPType.BINARY, INT_LONG_TYPES), + new VOP("EXPM1", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("FIRST_NONZERO", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("FMA", VOPType.TERNARY, FLOATING_TYPES), + new VOP("HYPOT", VOPType.BINARY, FLOATING_TYPES, false), // 1.5 ulp + new VOP("LEADING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("LOG", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("LOG10", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("LOG1P", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("LSHL", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("LSHR", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("MIN", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("MAX", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("MUL", VOPType.INTEGRAL_ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("NEG", VOPType.UNARY, PRIMITIVE_TYPES), + new VOP("NOT", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("OR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("POW", VOPType.BINARY, FLOATING_TYPES, false), // 1 ulp + new VOP("REVERSE", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("REVERSE_BYTES", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("ROL", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("ROR", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("SADD", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("SIN", VOPType.UNARY, FLOATING_TYPES, false), // 1 ulp + new VOP("SINH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp + new VOP("SQRT", VOPType.UNARY, FLOATING_TYPES), + new VOP("SSUB", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("SUADD", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("SUB", VOPType.BINARY, PRIMITIVE_TYPES), + new VOP("SUSUB", VOPType.BINARY, INTEGRAL_TYPES), + new VOP("TAN", VOPType.UNARY, FLOATING_TYPES, false), // 1.25 ulp + new VOP("TANH", VOPType.UNARY, FLOATING_TYPES, false), // 2.5 ulp + new VOP("TRAILING_ZEROS_COUNT", VOPType.UNARY, INTEGRAL_TYPES), + new VOP("UMAX", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("UMIN", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("XOR", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("ZOMO", VOPType.UNARY, INTEGRAL_TYPES) + ); + + private static final List VECTOR_CMP = List.of( + new VOP("EQ", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("GE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("GT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("LE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("LT", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("NE", VOPType.ASSOCIATIVE, PRIMITIVE_TYPES), + new VOP("UGE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("UGT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("ULE", VOPType.ASSOCIATIVE, INTEGRAL_TYPES), + new VOP("ULT", VOPType.ASSOCIATIVE, INTEGRAL_TYPES) + ); + + private static final List VECTOR_TEST = List.of( + new VOP("IS_DEFAULT", VOPType.UNARY, PRIMITIVE_TYPES), + new VOP("IS_NEGATIVE", VOPType.UNARY, PRIMITIVE_TYPES), + new VOP("IS_FINITE", VOPType.UNARY, FLOATING_TYPES), + new VOP("IS_NAN", VOPType.UNARY, FLOATING_TYPES), + new VOP("IS_INFINITE", VOPType.UNARY, FLOATING_TYPES) + ); + + // TODO: Conversion VectorOperators -> convertShape + + private static List generateVectorOperations() { + List ops = new ArrayList<>(); + + for (var type : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + // ----------------- IntVector, FloatVector, ... -------------------- + ops.add(Expression.make(type, "", type, ".abs()")); + ops.add(Expression.make(type, "", type, ".add(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".add(", type.elementType, ", ", type.maskType, ")")); + ops.add(Expression.make(type, "", type, ".add(", type, ")")); + ops.add(Expression.make(type, "", type, ".add(", type, ", ", type.maskType, ")")); + + // If VLENGTH*scale overflows, then a IllegalArgumentException is thrown. + ops.add(Expression.make(type, "", type, ".addIndex(1)")); + ops.add(Expression.make(type, "", type, ".addIndex(", INTS, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + + if (!type.elementType.isFloating()) { + ops.add(Expression.make(type, "", type, ".and(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".and(", type, ")")); + ops.add(Expression.make(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".bitwiseBlend(", type.elementType, ", ", type, ")")); + ops.add(Expression.make(type, "", type, ".bitwiseBlend(", type, ", ", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".bitwiseBlend(", type, ", ", type, ")")); + ops.add(Expression.make(type, "", type, ".not()")); + ops.add(Expression.make(type, "", type, ".or(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".or(", type, ")")); + } + + ops.add(Expression.make(type, "", type, ".blend(", type.elementType, ", ", type.maskType, ")")); + ops.add(Expression.make(type, "", type, ".blend(", LONGS, ", ", type.maskType, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".blend(", type, ", ", type.maskType, ")")); + + ops.add(Expression.make(type, type.name() + ".broadcast(" + type.speciesName + ", ", type.elementType, ")")); + ops.add(Expression.make(type, type.name() + ".broadcast(" + type.speciesName + ", ", LONGS, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + + for (var type2 : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + ops.add(Expression.make(type, "((" + type.name() + ")", type2 , ".castShape(" + type.speciesName + ", 0))")); + ops.add(Expression.make(type, "((" + type.name() + ")", type2 , ".castShape(" + type.speciesName + ", ", INTS, "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); + } + + // skip check + + for (VOP cmp : VECTOR_CMP) { + if (cmp.elementTypes().contains(type.elementType)) { + ops.add(Expression.make(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type.elementType, ")")); + ops.add(Expression.make(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type.elementType, ", ", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", LONGS, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + ops.add(Expression.make(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", LONGS, ", ", type.maskType, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + ops.add(Expression.make(type.maskType, "", type, ".compare(VectorOperators." + cmp.name() + ", ", type, ")")); + } + } + + ops.add(Expression.make(type, "", type, ".compress(", type.maskType, ")")); + + for (var type2 : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + // "convert" keeps the same shape, i.e. length of the vector in bits. + if (type.byteSize() == type2.byteSize()) { + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convert(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))")); + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convert(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class),", + INTS, // part + "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); + } + + // Reinterpretation FROM floating is not safe, because of different NaN encodings, i.e. + // we will not get deterministic results. + var reinterpretInfo = type2.elementType.isFloating() ? WITH_NONDETERMINISTIC_RESULT : new Expression.Info(); + + // The following "reinterpret" operations require same input and output shape. + if (type.byteSize() == type2.byteSize()) { + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convert(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), 0))", reinterpretInfo)); + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convert(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class),", + INTS, // part + "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); + if (type.elementType == BYTES) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsBytes()", reinterpretInfo)); + } + if (type.elementType == SHORTS) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsShorts()", reinterpretInfo)); + } + if (type.elementType == INTS) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsInts()", reinterpretInfo)); + } + if (type.elementType == LONGS) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsLongs()", reinterpretInfo)); + } + if (type.elementType == FLOATS) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsFloats()", reinterpretInfo)); + } + if (type.elementType == DOUBLES) { + ops.add(Expression.make(type, "", type2, ".reinterpretAsDoubles()", reinterpretInfo)); + } + if (type.elementType.isFloating() && type.elementType.byteSize() == type2.elementType.byteSize()) { + ops.add(Expression.make(type, "", type2, ".viewAsFloatingLanes()", reinterpretInfo)); + } + if (!type.elementType.isFloating() && type.elementType.byteSize() == type2.elementType.byteSize()) { + ops.add(Expression.make(type, "", type2, ".viewAsIntegralLanes()", reinterpretInfo)); + } + } + + // reinterpretShape + if (type2.byteSize() >= type.byteSize()) { + // Output overflows, is truncated (Expansion): part >= 0 + int partMask = type2.byteSize() / type.byteSize() - 1; + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".reinterpretShape(" + type.speciesName + ", ", + INTS, " & " + partMask + "))", reinterpretInfo)); + } else { + // Logical output too small to fill output vector (Contraction): part <= 0 + int partMask = type.byteSize() / type2.byteSize() - 1; + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".reinterpretShape(" + type.speciesName + ", " + + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); + } + + // convertShape - Cast/Reinterpret + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", ", + INTS, // part + "))", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", ", + INTS, // part + "))", reinterpretInfo.combineWith(WITH_OUT_OF_BOUNDS_EXCEPTION))); + // Compute size of logical output, before it is "fit" into the output vector. + // Each element is cast/reinterpret individually, and so the logical output + // has the lane count of the input vector, and the element size that of the output element size. + // Note: reinterpret of float -> long means we expand each element from 4->8 bytes, and so + // we take the lower 4 bytes from the float and add 4 bytes of zero padding. + int conversionLogicalByteSize = type2.length * type.elementType.byteSize(); + if (conversionLogicalByteSize >= type.byteSize()) { + // Output overflows, is truncated (Expansion): part >= 0 + int partMask = conversionLogicalByteSize / type.byteSize() - 1; + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", ", + INTS, " & " + partMask + "))")); + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", ", + INTS, " & " + partMask + "))", reinterpretInfo)); + } else { + // Logical output too small to fill output vector (Contraction): part <= 0 + int partMask = type.byteSize() / conversionLogicalByteSize - 1; + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofCast(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", " + + "-(", INTS, " & " + partMask + ")))")); + ops.add(Expression.make(type, + "((" + type.name() + ")", + type2, + ".convertShape(VectorOperators.Conversion.ofReinterpret(" + + type2.elementType.name() + ".class, " + + type.elementType.name() + ".class), " + + type.speciesName + ", " + + "-(", INTS, " & " + partMask + ")))", reinterpretInfo)); + } + // TODO: convertShape - using VectorOperators.I2S,REINTERPRET_I2F,ZERO_EXTEND_B2I etc. + } + + ops.add(Expression.make(type, "", type, ".div(", type.elementType, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".div(", type.elementType, ", ", type.maskType, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".div(", type, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".div(", type, ", ", type.maskType, ")", WITH_ARITHMETIC_EXCEPTION)); + + ops.add(Expression.make(type.maskType, "", type, ".eq(", type.elementType, ")")); + ops.add(Expression.make(type.maskType, "", type, ".eq(", type, ")")); + // skip equals + ops.add(Expression.make(type, "", type, ".expand(", type.maskType, ")")); + // skip fromArray + // skip fromMemorySegment + // skip hashCode + // skip intoArray + // skip intoMemorySegment + // TODO: memory accesses. It is not clear yet if these are to be modeled as Expressions, or rather statements. + ops.add(Expression.make(type.elementType, "", type, ".lane(", INTS, " & " + (type.length-1) + ")")); + ops.add(Expression.make(type.elementType, "", type, ".lane(", INTS, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + + for (VOP vop : VECTOR_OPS) { + var vopInfo = vop.isDeterministic ? new Expression.Info() : WITH_NONDETERMINISTIC_RESULT; + if (vop.elementTypes().contains(type.elementType)) { + switch(vop.type()) { + case VOPType.UNARY: + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.maskType, ")", vopInfo)); + break; + case VOPType.ASSOCIATIVE: + case VOPType.INTEGRAL_ASSOCIATIVE: + if (vop.type() == VOPType.ASSOCIATIVE || !type.elementType.isFloating()) { + ops.add(Expression.make(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ")", vopInfo)); + ops.add(Expression.make(type.elementType, "", type, ".reduceLanes(VectorOperators." + vop.name() + ", ", type.maskType, ")", vopInfo)); + } + // fall-through + case VOPType.BINARY: + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.maskType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", LONGS, ")", vopInfo.combineWith(WITH_ILLEGAL_ARGUMENT_EXCEPTION))); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", LONGS, ", ", type.maskType, ")", vopInfo.combineWith(WITH_ILLEGAL_ARGUMENT_EXCEPTION))); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.maskType, ")", vopInfo)); + break; + case VOPType.TERNARY: + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type.elementType, ", ", type.maskType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type.elementType, ", ", type, ", ", type.maskType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type.elementType, ", ", type.maskType, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ")", vopInfo)); + ops.add(Expression.make(type, "", type, ".lanewise(VectorOperators." + vop.name() + ", ", type, ", ", type, ", ", type.maskType, ")", vopInfo)); + break; + } + } + } + + ops.add(Expression.make(type.maskType, "", type, ".lt(", type.elementType, ")")); + ops.add(Expression.make(type.maskType, "", type, ".lt(", type, ")")); + + ops.add(Expression.make(type.maskType, "", type, ".maskAll(", BOOLEANS, ")")); + + ops.add(Expression.make(type, "", type, ".max(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".max(", type, ")")); + ops.add(Expression.make(type, "", type, ".min(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".min(", type, ")")); + + ops.add(Expression.make(type, "", type, ".mul(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".mul(", type.elementType, ", ", type.maskType, ")")); + ops.add(Expression.make(type, "", type, ".mul(", type, ")")); + ops.add(Expression.make(type, "", type, ".mul(", type, ", ", type.maskType, ")")); + + ops.add(Expression.make(type, "", type, ".neg()")); + + ops.add(Expression.make(type, "", type, ".rearrange(", type.shuffleType, ")")); + ops.add(Expression.make(type, "", type, ".rearrange(", type.shuffleType, ", ", type, ")")); + ops.add(Expression.make(type, "", type, ".rearrange(", type.shuffleType, ", ", type.maskType, ")")); + + ops.add(Expression.make(type, "", type, ".selectFrom(", type, ")")); + ops.add(Expression.make(type, "", type, ".selectFrom(", type, ", ", type, ")")); + ops.add(Expression.make(type, "", type, ".selectFrom(", type, ", ", type.maskType, ")")); + + ops.add(Expression.make(type, "", type, ".slice(", INTS, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".slice(", INTS, ", ", type, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".slice(", INTS, ", ", type, ", ", type.maskType, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + + ops.add(Expression.make(type, "", type, ".sub(", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".sub(", type.elementType, ", ", type.maskType, ")")); + ops.add(Expression.make(type, "", type, ".sub(", type, ")")); + ops.add(Expression.make(type, "", type, ".sub(", type, ", ", type.maskType, ")")); + + + for (VOP test : VECTOR_TEST) { + if (test.elementTypes().contains(type.elementType)) { + ops.add(Expression.make(type.maskType, "", type, ".test(VectorOperators." + test.name() + ")")); + ops.add(Expression.make(type.maskType, "", type, ".test(VectorOperators." + test.name() + ", ", type.maskType, ")")); + } + } + + // skip toArray and friends + + ops.add(Expression.make(type, "", type, ".unslice(", INTS, " & " + (type.length-1) + ")")); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, " & " + (type.length-1) + ", ", type, ", ", INTS, " & 2)")); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, ", ", type, ", 0)", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, ", ", type, ", ", INTS, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, ", ", type, ", 0, ", type.maskType, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type, "", type, ".unslice(", INTS, ", ", type, ", ", INTS, ", ", type.maskType, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + + ops.add(Expression.make(type, "", type, ".withLane(", INTS, ", ", type.elementType, ")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + + if (type.elementType.isFloating()) { + ops.add(Expression.make(type, "", type, ".fma(", type.elementType, ", ", type.elementType, ")")); + ops.add(Expression.make(type, "", type, ".fma(", type, ", ", type, ")")); + + // TODO: enforce precision instead of just making it non-deterministic? + // See similar comment at VOP definition. + ops.add(Expression.make(type, "", type, ".pow(", type.elementType, ")", WITH_NONDETERMINISTIC_RESULT)); + ops.add(Expression.make(type, "", type, ".pow(", type, ")", WITH_NONDETERMINISTIC_RESULT)); + ops.add(Expression.make(type, "", type, ".sqrt()")); + } + + ops.add(Expression.make(type.shuffleType, "", type, ".toShuffle()")); + // skip zero - can get it from type.con() anyway. + + // ----------------- MaskVector -------------------- + // skip fromValues, too many inputs + // skip fromArray + ops.add(Expression.make(type.maskType, "VectorMask.fromLong(" + type.speciesName + ", ", LONGS, ")")); + for (var type2 : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + var mask = type.maskType; + var mask2 = type2.maskType; + if (type.length == type2.length) { + ops.add(Expression.make(mask, "((" + mask.name() + ")", mask2 , ".cast(" + type.speciesName + "))")); + } + } + ops.add(Expression.make(LONGS, "", type.maskType , ".toLong()")); + // skip toArray + // skip intoArray + ops.add(Expression.make(BOOLEANS, "", type.maskType , ".anyTrue()")); + ops.add(Expression.make(BOOLEANS, "", type.maskType , ".allTrue()")); + ops.add(Expression.make(INTS, "", type.maskType , ".trueCount()")); + ops.add(Expression.make(INTS, "", type.maskType , ".firstTrue()")); + ops.add(Expression.make(INTS, "", type.maskType , ".lastTrue()")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".and(", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".or(", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".xor(", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".andNot(", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".eq(", type.maskType, ")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".not()")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".indexInRange(", INTS, ", ", INTS,")")); + ops.add(Expression.make(type.maskType, "", type.maskType , ".indexInRange(", LONGS, ", ", LONGS,")")); + ops.add(Expression.make(type, "((" + type.name() + ")", type.maskType , ".toVector())")); + ops.add(Expression.make(BOOLEANS, "", type.maskType , ".laneIsSet(", INTS, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(BOOLEANS, "", type.maskType , ".laneIsSet(", INTS, " & " + (type.length-1) + ")")); + // skip check + // skip toString + // skip equals + // skip hashCode + ops.add(Expression.make(type.maskType, "", type.maskType , ".compress()")); + + // ----------------- ShuffleVector -------------------- + for (var type2 : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + var shuffle = type.shuffleType; + var shuffle2 = type2.shuffleType; + if (type.length == type2.length) { + ops.add(Expression.make(shuffle, "((" + shuffle.name() + ")", shuffle2 , ".cast(" + type.speciesName + "))")); + } + } + ops.add(Expression.make(INTS, "", type.shuffleType , ".checkIndex(", INTS, ")", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(INTS, "", type.shuffleType , ".wrapIndex(", INTS, ")")); + ops.add(Expression.make(type.shuffleType, "", type.shuffleType , ".checkIndexes()", WITH_OUT_OF_BOUNDS_EXCEPTION)); + ops.add(Expression.make(type.shuffleType, "", type.shuffleType , ".wrapIndexes()")); + ops.add(Expression.make(type.maskType, "", type.shuffleType , ".laneIsValid()")); + // skip fromValues, too many inputs + // skip fromArray + // skip fromMemorySegment + // skip fromOp + // skip iota + // skip makeZip + // skip makeUnzip + // skip toArray + // skip intoArray + // skip intoMemorySegment + ops.add(Expression.make(type, "((" + type.name() + ")", type.shuffleType , ".toVector())")); + ops.add(Expression.make(INTS, "", type.shuffleType , ".laneSource(", INTS,")", WITH_ILLEGAL_ARGUMENT_EXCEPTION)); + ops.add(Expression.make(INTS, "", type.shuffleType , ".laneSource(", INTS," & " + (type.length-1) + ")")); + ops.add(Expression.make(type.shuffleType, "", type.shuffleType, ".rearrange(", type.shuffleType, ")")); + // skip toString + // skip equals + // skip hashCode + } + + // TODO: VectorSpecies API methods + + // Make sure the list is not modifiable. + return List.copyOf(ops); + } + + /** + * Provides a lits of operations on {@link PrimitiveType}s, such as arithmetic, logical, + * and cast operations. + */ + public static final List PRIMITIVE_OPERATIONS = generatePrimitiveOperations(); + + public static final List FLOAT16_OPERATIONS = generateFloat16Operations(); + + public static final List SCALAR_NUMERIC_OPERATIONS = Utils.concat( + PRIMITIVE_OPERATIONS, + FLOAT16_OPERATIONS + ); + + public static final List VECTOR_OPERATIONS = generateVectorOperations(); + + public static final List ALL_OPERATIONS = Utils.concat( + SCALAR_NUMERIC_OPERATIONS, + VECTOR_OPERATIONS + ); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index b20dbb28d22..cd796fd0d31 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -34,6 +34,7 @@ import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; /** * The {@link PrimitiveType} models Java's primitive types, and provides a set @@ -281,6 +282,22 @@ public static double nextDouble() { public static boolean nextBoolean() { return RANDOM.nextBoolean(); } + + """, + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(type -> scope( + let("type", type), + """ + public static void fill(#type[] a) { + for (int i = 0; i < a.length; i++) { + """, + " a[i] = ", type.callLibraryRNG(), ";\n", + """ + } + } + """ + )).toList(), + """ + } """ )); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Utils.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Utils.java new file mode 100644 index 00000000000..b96ad830a4b --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Utils.java @@ -0,0 +1,41 @@ +/* + * 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. + */ + +package compiler.lib.template_framework.library; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Package private class with some helpful utility methods, used in multiple + * places of the template library. + */ +final class Utils { + @SafeVarargs + public static List concat(List... lists) { + return Arrays.stream(lists) + .flatMap(List::stream) + .collect(Collectors.toList()); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java new file mode 100644 index 00000000000..7eabd42a723 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/VectorType.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.template_framework.library; + +import java.util.List; +import java.util.stream.Stream; +import java.util.Random; +import jdk.test.lib.Utils; + +import compiler.lib.template_framework.DataName; +import static compiler.lib.template_framework.library.PrimitiveType.BYTES; +import static compiler.lib.template_framework.library.PrimitiveType.SHORTS; +import static compiler.lib.template_framework.library.PrimitiveType.INTS; +import static compiler.lib.template_framework.library.PrimitiveType.LONGS; +import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; +import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; +import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; + +/** + * The {@link VectorType} models the Vector API types. + */ +public abstract class VectorType implements CodeGenerationDataNameType { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static final VectorType.Vector BYTE_64 = new VectorType.Vector(BYTES, 8); + public static final VectorType.Vector BYTE_128 = new VectorType.Vector(BYTES, 16); + public static final VectorType.Vector BYTE_256 = new VectorType.Vector(BYTES, 32); + public static final VectorType.Vector BYTE_512 = new VectorType.Vector(BYTES, 64); + + public static final VectorType.Vector SHORT_64 = new VectorType.Vector(SHORTS, 4); + public static final VectorType.Vector SHORT_128 = new VectorType.Vector(SHORTS, 8); + public static final VectorType.Vector SHORT_256 = new VectorType.Vector(SHORTS, 16); + public static final VectorType.Vector SHORT_512 = new VectorType.Vector(SHORTS, 32); + + public static final VectorType.Vector INT_64 = new VectorType.Vector(INTS, 2); + public static final VectorType.Vector INT_128 = new VectorType.Vector(INTS, 4); + public static final VectorType.Vector INT_256 = new VectorType.Vector(INTS, 8); + public static final VectorType.Vector INT_512 = new VectorType.Vector(INTS, 16); + + public static final VectorType.Vector LONG_64 = new VectorType.Vector(LONGS, 1); + public static final VectorType.Vector LONG_128 = new VectorType.Vector(LONGS, 2); + public static final VectorType.Vector LONG_256 = new VectorType.Vector(LONGS, 4); + public static final VectorType.Vector LONG_512 = new VectorType.Vector(LONGS, 8); + + public static final VectorType.Vector FLOAT_64 = new VectorType.Vector(FLOATS, 2); + public static final VectorType.Vector FLOAT_128 = new VectorType.Vector(FLOATS, 4); + public static final VectorType.Vector FLOAT_256 = new VectorType.Vector(FLOATS, 8); + public static final VectorType.Vector FLOAT_512 = new VectorType.Vector(FLOATS, 16); + + public static final VectorType.Vector DOUBLE_64 = new VectorType.Vector(DOUBLES, 1); + public static final VectorType.Vector DOUBLE_128 = new VectorType.Vector(DOUBLES, 2); + public static final VectorType.Vector DOUBLE_256 = new VectorType.Vector(DOUBLES, 4); + public static final VectorType.Vector DOUBLE_512 = new VectorType.Vector(DOUBLES, 8); + + private final String vectorTypeName; + + private VectorType(String vectorTypeName) { + this.vectorTypeName = vectorTypeName; + } + + @Override + public final String name() { + return vectorTypeName; + } + + @Override + public String toString() { + return name(); + } + + @Override + public boolean isSubtypeOf(DataName.Type other) { + // Each type has a unique instance. + return this == other; + } + + private static final String vectorTypeName(PrimitiveType elementType) { + return switch(elementType.name()) { + case "byte" -> "ByteVector"; + case "short" -> "ShortVector"; + case "char" -> throw new UnsupportedOperationException("VectorAPI has no char vector type"); + case "int" -> "IntVector"; + case "long" -> "LongVector"; + case "float" -> "FloatVector"; + case "double" -> "DoubleVector"; + default -> throw new UnsupportedOperationException("Not supported: " + elementType.name()); + }; + } + + public static final class Vector extends VectorType { + public final PrimitiveType elementType; + public final int length; // lane count + public final String speciesName; + + public final Mask maskType; + public final Shuffle shuffleType; + + private Vector(PrimitiveType elementType, int length) { + super(vectorTypeName(elementType)); + this.elementType = elementType; + this.length = length; + this.speciesName = name() + ".SPECIES_" + (byteSize() * 8); + this.maskType = new Mask(this); + this.shuffleType = new Shuffle(this); + } + + @Override + public final Object con() { + int r = RANDOM.nextInt(64); + if (r == 0) { + return List.of(name(), ".zero(", speciesName, ")"); + } else if (r <= 8) { + return List.of( + name(), ".fromArray(", speciesName, ", new ", elementType.name(), "[] {", + elementType.con(), + Stream.generate(() -> + List.of(", ", elementType.con()) + ).limit(length - 1).toList(), + "}, 0)" + ); + } else { + return List.of(name(), ".broadcast(", speciesName, ", ", elementType.con(), ")"); + } + } + + public int byteSize() { + return elementType.byteSize() * length; + } + } + + public static final class Mask extends VectorType { + public final Vector vectorType; + + private Mask(Vector vectorType) { + super("VectorMask<" + vectorType.elementType.boxedTypeName() + ">"); + this.vectorType = vectorType; + } + + @Override + public final Object con() { + int r = RANDOM.nextInt(64); + if (r <= 8) { + return List.of( + "VectorMask.fromArray(", vectorType.speciesName, ", new boolean[] {", + BOOLEANS.con(), + Stream.generate(() -> + List.of(", ", BOOLEANS.con()) + ).limit(vectorType.length - 1).toList(), + "}, 0)" + ); + } else if (r <= 16) { + return List.of( + "VectorMask.fromValues(", vectorType.speciesName, + Stream.generate(() -> + List.of(", ", BOOLEANS.con()) + ).limit(vectorType.length).toList(), + ")" + ); + } else if (r <= 24) { + // All true or all false + var tf = BOOLEANS.con(); + return List.of( + "VectorMask.fromValues(", vectorType.speciesName, + Stream.generate(() -> + List.of(", ", tf) + ).limit(vectorType.length).toList(), + ")" + ); + } else { + return List.of("VectorMask.fromLong(", vectorType.speciesName, ", ", LONGS.con(), ")"); + } + } + } + + public static final class Shuffle extends VectorType { + public final Vector vectorType; + + private Shuffle(Vector vectorType) { + super("VectorShuffle<" + vectorType.elementType.boxedTypeName() + ">"); + this.vectorType = vectorType; + } + + @Override + public final Object con() { + int r = RANDOM.nextInt(64); + if (r <= 8) { + return List.of( + "VectorShuffle.fromArray(", vectorType.speciesName, ", new int[] {", + INTS.con(), + Stream.generate(() -> + List.of(", ", INTS.con()) + ).limit(vectorType.length - 1).toList(), + "}, 0)" + ); + } else if (r <= 16) { + return List.of( + "VectorShuffle.fromValues(", vectorType.speciesName, + Stream.generate(() -> + List.of(", ", INTS.con()) + ).limit(vectorType.length).toList(), + ")" + ); + } else if (r <= 24) { + return List.of("VectorShuffle.makeZip(", vectorType.speciesName, ", ", RANDOM.nextInt(2), ")"); + } else if (r <= 32) { + return List.of("VectorShuffle.makeUnzip(", vectorType.speciesName, ", ", RANDOM.nextInt(2), ")"); + } else if (r <= 40) { + return List.of("VectorShuffle.fromOp(", vectorType.speciesName, ", i -> ", + INTS.con(), " + i * ", INTS.con(), " + i * i * ", INTS.con(), ")"); + } else { + return List.of("VectorShuffle.iota(", vectorType.speciesName, ", ", + INTS.con(), ", ", INTS.con(), ", true)"); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java index fd0143acdb6..764acba567f 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopLimitChecks.java @@ -37,6 +37,7 @@ * @summary test loop limit checks are inserted when stressing int counted loops to long counted loops * @library /test/lib * @requires vm.debug == true + * @requires vm.flagless * @run driver compiler.loopopts.TestStressLongCountedLoopLimitChecks */ public class TestStressLongCountedLoopLimitChecks { diff --git a/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java index f7ed6096080..337012d834f 100644 --- a/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java +++ b/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java @@ -28,6 +28,7 @@ * @library /test/lib / * * @requires os.arch=="aarch64" + * @requires vm.flagless * * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java b/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java new file mode 100644 index 00000000000..c46f1ecab98 --- /dev/null +++ b/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java @@ -0,0 +1,434 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package compiler.runtime; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; + +/* + * @test + * @bug 8370947 8381003 + * @summary Test command-line options for UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation + * @library /test/lib + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.runtime.TestDeferredICacheInvalidationCmdOptions + */ + +public class TestDeferredICacheInvalidationCmdOptions { + + // CPU identifiers + private static final int CPU_ARM = 0x41; + private static final int NEOVERSE_N1_MODEL = 0xd0c; + + // Known ARM Neoverse models where we can predict UseSingleICacheInvalidation + // behavior. + private static final int[] KNOWN_NEOVERSE_MODELS = { + NEOVERSE_N1_MODEL, + 0xd40, // Neoverse V1 + 0xd49, // Neoverse N2 + 0xd4f, // Neoverse V2 + 0xd83, // Neoverse V3AE + 0xd84, // Neoverse V3 + 0xd8e, // Neoverse N3 + }; + + private static boolean isAffected; + private static boolean isKnownModel; + + public static void main(String[] args) throws Exception { + // This test does not depend on CPU identification — run it first. + testDisableBothFlags(); + + // Parse CPU features and print CPU info + parseCPUFeatures(); + + if (!isKnownModel) { + throw new SkippedException("Unknown CPU model - skipping remaining tests."); + } + + if (isAffected) { + // Detect whether IC IVAU is trapped on this system. + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (output.getExitValue() != 0) { + // Verify the failure is the expected probe error, not something else. + output.shouldContain("IC IVAU is not trapped"); + throw new SkippedException("IC IVAU is not trapped - skipping remaining tests."); + } else { + System.out.println("IC IVAU trap active."); + } + } + + if (isAffected) { + // Check defaults on Neoverse N1 pre-r4p1 + testCase_DefaultsOnNeoverseN1(); + + // Check if NeoverseN1ICacheErratumMitigation is set to false on affected CPUs, + // UseSingleICacheInvalidation is also set to false + testCase_ExplicitlyDisableErrataAffectsDeferred(); + + // Check JVM error if UseSingleICacheInvalidation=true + // but NeoverseN1ICacheErratumMitigation=false on affected CPUs + testCase_ConflictingFlagsOnAffectedCPUs(); + + // Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation + testCase_ExplicitlyEnableErrataEnablesDeferred(); + + // Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true + testCase_ConflictingErrataWithoutDeferred(); + } else { + // Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs + testCase_DefaultsOnUnaffectedCPUs(); + + // Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error + testCase_EnablingErrataOnUnaffectedCPU(); + } + + System.out.println("All test cases passed!"); + } + + /** + * Parse CPU features string from WhiteBox.getCPUFeatures() to extract: + * - cpuFamily: 0x41 for ARM + * - cpuVariant: major revision + * - cpuModel: e.g., 0xd0c for Neoverse N1 + * - cpuRevision: minor revision + * - cpuModel2: secondary model (if present, in parentheses) + * + * Sets the static fields isAffected and isKnownModel, and prints CPU info. + * + * Format: 0x%02x:0x%x:0x%03x:%d[(0x%03x)] + * Example: "0x41:0x3:0xd0c:0" or "0x41:0x3:0xd0c:0(0xd0c)" + * + * @throws SkippedException if not running on ARM CPU + */ + private static void parseCPUFeatures() { + WhiteBox wb = WhiteBox.getWhiteBox(); + String cpuFeatures = wb.getCPUFeatures(); + System.out.println("CPU Features string: " + cpuFeatures); + + if (cpuFeatures == null || cpuFeatures.isEmpty()) { + throw new RuntimeException("No CPU features available"); + } + + int commaIndex = cpuFeatures.indexOf(","); + if (commaIndex == -1) { + throw new RuntimeException("Unexpected CPU features format (no comma): " + cpuFeatures); + } + + String cpuPart = cpuFeatures.substring(0, commaIndex).trim(); + + String[] parts = cpuPart.split(":"); + if (parts.length < 4) { + throw new RuntimeException("Unexpected CPU features format: " + cpuPart); + } + + int cpuFamily = Integer.parseInt(parts[0].substring(2), 16); + if (cpuFamily != CPU_ARM) { + throw new SkippedException("Not running on ARM CPU (cpuFamily=0x" + Integer.toHexString(cpuFamily) + ")"); + } + + int cpuVariant = Integer.parseInt(parts[1].substring(2), 16); + int cpuModel = Integer.parseInt(parts[2].substring(2), 16); + int cpuModel2 = 0; + + int model2Start = parts[3].indexOf("("); + String revisionStr = parts[3]; + if (model2Start != -1) { + if (!parts[3].endsWith(")")) { + throw new RuntimeException("Unexpected CPU features format (missing closing parenthesis): " + parts[3]); + } + String model2Str = parts[3].substring(model2Start + 1, parts[3].length() - 1); + cpuModel2 = Integer.parseInt(model2Str.substring(2), 16); + revisionStr = parts[3].substring(0, model2Start); + } + int cpuRevision = Integer.parseInt(revisionStr); + + // Neoverse N1 errata 1542419 affects r3p0, r3p1 and r4p0. + // It is fixed in r4p1 and later revisions. + if (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL) { + isAffected = (cpuVariant == 3 && cpuRevision == 0) || + (cpuVariant == 3 && cpuRevision == 1) || + (cpuVariant == 4 && cpuRevision == 0); + } + + // Check if this is a known Neoverse model. + isKnownModel = false; + for (int model : KNOWN_NEOVERSE_MODELS) { + if (cpuModel == model || cpuModel2 == model) { + isKnownModel = true; + break; + } + } + + printCPUInfo(cpuFamily, cpuVariant, cpuModel, cpuModel2, cpuRevision); + } + + private static void printCPUInfo(int cpuFamily, int cpuVariant, int cpuModel, int cpuModel2, int cpuRevision) { + boolean isNeoverseN1 = (cpuFamily == CPU_ARM) && + (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL); + System.out.println("\n=== CPU Information ==="); + System.out.println("CPU Family: 0x" + Integer.toHexString(cpuFamily)); + System.out.println("CPU Variant: 0x" + Integer.toHexString(cpuVariant)); + System.out.println("CPU Model: 0x" + Integer.toHexString(cpuModel)); + if (cpuModel2 != 0) { + System.out.println("CPU Model2: 0x" + Integer.toHexString(cpuModel2)); + } + System.out.println("CPU Revision: " + cpuRevision); + System.out.println("Is Neoverse N1: " + isNeoverseN1); + System.out.println("Is affected by errata 1542419: " + isAffected); + System.out.println("Is known model: " + isKnownModel); + System.out.println("======================\n"); + } + + /** + * Test that UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation flags + * can be explicitly set to false on any system. + */ + private static void testDisableBothFlags() throws Exception { + System.out.println("\nTest: Explicitly disable both flags"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-UseSingleICacheInvalidation", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); + boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); + + System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); + System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); + + if (!hasErrataDisabled) { + throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); + } + + if (!hasSingleDisabled) { + throw new RuntimeException("Failed to disable UseSingleICacheInvalidation via command line"); + } + + System.out.println("Successfully disabled both flags"); + } + + /** + * Check defaults on Neoverse N1 affected revisions. + * UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation flags should be true. + */ + private static void testCase_DefaultsOnNeoverseN1() throws Exception { + System.out.println("\nTest: Check defaults on Neoverse N1 affected revisions"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + + if (!hasSingleEnabled || !hasErrataEnabled) { + throw new RuntimeException("On affected Neoverse N1 with trap active, " + + "UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation flags should be enabled by default"); + } + System.out.println("Correctly enabled on affected Neoverse N1"); + } + + /** + * Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs. + */ + private static void testCase_DefaultsOnUnaffectedCPUs() throws Exception { + System.out.println("\nTest: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + + if (hasErrataEnabled) { + throw new RuntimeException("On unaffected CPUs, NeoverseN1ICacheErratumMitigation should be disabled"); + } + System.out.println("Correctly disabled on unaffected CPU"); + } + + /** + * Check if NeoverseN1ICacheErratumMitigation is set to false via cmd on affected CPUs, + * UseSingleICacheInvalidation is set to false. + */ + private static void testCase_ExplicitlyDisableErrataAffectsDeferred() throws Exception { + System.out.println("\nTest: Explicitly disable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); + boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); + System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); + + if (!hasErrataDisabled) { + throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); + } + + if (!hasSingleDisabled) { + throw new RuntimeException("On affected CPU, disabling NeoverseN1ICacheErratumMitigation should also disable UseSingleICacheInvalidation"); + } + System.out.println("Correctly synchronized on affected CPU"); + } + + /** + * Check JVM reports an error if UseSingleICacheInvalidation is set to true + * but NeoverseN1ICacheErratumMitigation is set to false on affected CPUs. + */ + private static void testCase_ConflictingFlagsOnAffectedCPUs() throws Exception { + System.out.println("\nTest: Try to set UseSingleICacheInvalidation=true with NeoverseN1ICacheErratumMitigation=false"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UseSingleICacheInvalidation", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (output.getExitValue() == 0) { + throw new RuntimeException("On affected CPU, setting UseSingleICacheInvalidation=true " + + "with NeoverseN1ICacheErratumMitigation=false should cause error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected conflicting flags on affected CPU"); + } + + /** + * Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation. + */ + private static void testCase_ExplicitlyEnableErrataEnablesDeferred() throws Exception { + System.out.println("\nTest: Explicitly enable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); + + if (!hasErrataEnabled) { + throw new RuntimeException("Failed to enable NeoverseN1ICacheErratumMitigation via command line"); + } + + if (!hasSingleEnabled) { + throw new RuntimeException("On affected CPU, enabling NeoverseN1ICacheErratumMitigation should also enable UseSingleICacheInvalidation"); + } + System.out.println("Correctly synchronized on affected CPU"); + } + + /** + * Check JVM reports an error if UseSingleICacheInvalidation is set to false + * and NeoverseN1ICacheErratumMitigation is set to true on affected CPUs. + */ + private static void testCase_ConflictingErrataWithoutDeferred() throws Exception { + System.out.println("\nTest: Try to set NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-UseSingleICacheInvalidation", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (output.getExitValue() == 0) { + throw new RuntimeException("On affected CPU, setting NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false should cause an error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected conflicting flags on affected CPU"); + } + + /** + * Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error. + */ + private static void testCase_EnablingErrataOnUnaffectedCPU() throws Exception { + System.out.println("\nTest: Try to set NeoverseN1ICacheErratumMitigation=true on unaffected CPU"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (output.getExitValue() == 0) { + throw new RuntimeException("On unaffected CPU, setting NeoverseN1ICacheErratumMitigation=true should cause error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected enabling errata flag on unaffected CPU"); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java new file mode 100644 index 00000000000..ea36cca4b92 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorExpressionFuzzer.java @@ -0,0 +1,345 @@ +/* + * 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=AVX2 + * @bug 8369699 + * @key randomness + * @summary Test the Template Library's expression generation for the Vector API. + * @requires os.family == "linux" & os.simpleArch == "x64" + * @modules jdk.incubator.vector + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../compiler/lib/verify/Verify.java + * @run driver ${test.main.class} -XX:UseAVX=2 + */ + +// TODO: remove the x64 and linux restriction above. I added that for now so we are not flooded +// with failures in the CI. We should remove these restriction once more bugs are fixed. +// x64 linux is the easiest to debug on for me, that's why I picked it. +// In addition, I put a UseAVX=2 restriction below, to avoid AVX512 bugs for now. +// +// A trick to extend this to other platforms: create a new run block, so you have full +// freedom to restrict it as necessary for platform and vector features. + +package compiler.vectorapi; + +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Set; +import java.util.Random; +import jdk.test.lib.Utils; +import java.util.stream.IntStream; + +import compiler.lib.compile_framework.CompileFramework; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; +import compiler.lib.template_framework.library.Operations; +import compiler.lib.template_framework.library.TestFrameworkClass; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.VectorType; + +/** + * Basic Expression Fuzzer for Vector API. + */ +public class VectorExpressionFuzzer { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("compiler.vectorapi.templated.Templated", generate(comp)); + + // Compile the source file. + comp.compile("--add-modules=jdk.incubator.vector"); + + List vmArgs = new ArrayList<>(List.of( + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED" + )); + vmArgs.addAll(Arrays.asList(args)); // Forward args + String[] vmArgsArray = vmArgs.toArray(new String[0]); + + // compiler.vectorapi.templated.InnterTest.main(new String[] {}); + comp.invoke("compiler.vectorapi.templated.Templated", "main", new Object[] { vmArgsArray } ); + } + + // For Example 2 below. + record TestArgument( + Object defineAndFill, // used once: to define and fill the input value + Object passMethodArgument, // used twice: to pass argument to test and reference + Object receiveMethodArgument, // used twice: to receive argument in test and reference + Object use) {} // used twice: to use the value as expression argument + + // Generate a source Java file as String + public static String generate(CompileFramework comp) { + // Generate a list of test methods. + List tests = new ArrayList<>(); + + // We are going to use some random numbers in our tests, so import some good methods for that. + tests.add(PrimitiveType.generateLibraryRNG()); + + // Example 1: + // To start simple, we just call the expression with the same constant arguments + // every time. We can still compare the "gold" value obtained via interpreter with + // later results obtained from the compiled method. + var template1 = Template.make("type", (VectorType.Vector type) -> { + // The depth determines roughly how many operations are going to be used in the expression. + int depth = RANDOM.nextInt(1, 10); + Expression expression = Expression.nestRandomly(type, Operations.ALL_OPERATIONS, depth, Nesting.EXACT); + List expressionArguments = expression.argumentTypes.stream().map(CodeGenerationDataNameType::con).toList(); + return scope( + """ + // --- $test start --- + // Using $GOLD + // type: #type + + static final Object $GOLD = $test(); + + @Test + public static Object $test() { + try { + """, + " return ", expression.asToken(expressionArguments), ";\n", + expression.info.exceptions.stream().map(exception -> + " } catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just so javac is happy if there are no exceptions to catch. + } + } + + @Check(test = "$test") + public static void $check(Object result) { + """, + expression.info.isResultDeterministic + ? " Verify.checkEQ(result, $GOLD);\n" + : " // result not deterministic - don't verify.\n", + """ + } + + // --- $test end --- + """ + ); + }); + + // Example 2: + // Now a more complicated case. We want: + // - For every invocation of the test method, we want to have different inputs for the arguments. + // - We check correctness with a reference method that does the same but runs in the interpreter. + // - Input values are delivered via fields or array loads. + // - The final vector is written into an array, and that array is returned. + var template2Body = Template.make("expression", "arguments", (Expression expression, List arguments) -> scope( + let("elementType", ((VectorType.Vector)expression.returnType).elementType), + """ + try { + #elementType[] out = new #elementType[1000]; + """, + expression.asToken(arguments), ".intoArray(out, 0);\n", + "return out;\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just javac is happy if there are no exceptions to catch. + } + """ + )); + + var template2 = Template.make("type", (VectorType.Vector type) -> { + // The depth determines roughly how many operations are going to be used in the expression. + int depth = RANDOM.nextInt(1, 10); + Expression expression = Expression.nestRandomly(type, Operations.ALL_OPERATIONS, depth, Nesting.EXACT); + + List arguments = new ArrayList<>(); + for (int i = 0; i < expression.argumentTypes.size(); i++) { + String name = "arg_" + i; + CodeGenerationDataNameType argumentType = expression.argumentTypes.get(i); + switch(RANDOM.nextInt(4)) { + case 0 -> { + // Use the constant directly, no argument passing needed. + // To make the logic of passing arguments easy, we just pass null and receive an unused argument anyway. + arguments.add(new TestArgument( + "", + "null", + "Object unused_" + i, + argumentType.con() + )); + } + case 1 -> { + // Create the constant outside, and pass it. + arguments.add(new TestArgument( + List.of(argumentType.name(), " ", name, " = ", argumentType.con(), ";\n"), + name, + List.of(argumentType.name(), " ", name), + name + )); + } + default -> { + if (argumentType instanceof PrimitiveType t) { + // We can use the LibraryRGN to create a new value for the primitive in each + // invocation. We have to make sure to call the LibraryRNG in the "defineAndFill", + // so we get the same value for both test and reference. If we called LibraryRNG + // for "use", we would get separate values, which is not helpful. + arguments.add(new TestArgument( + List.of(t.name(), " ", name, " = ", t.callLibraryRNG(), ";\n"), + name, + List.of(t.name(), " ", name), + name + )); + } else if (argumentType instanceof VectorType.Vector t) { + PrimitiveType et = t.elementType; + arguments.add(new TestArgument( + List.of(et.name(), "[] ", name, " = new ", et.name(), "[1000];\n", + "LibraryRNG.fill(", name,");\n"), + name, + List.of(et.name(), "[] ", name), + List.of(t.name(), ".fromArray(", t.speciesName, ", ", name, ", 0)") + )); + } else if (argumentType instanceof VectorType.Mask t) { + arguments.add(new TestArgument( + List.of("boolean[] ", name, " = new boolean[1000];\n", + "LibraryRNG.fill(", name,");\n"), + name, + List.of("boolean[] ", name), + List.of("VectorMask.fromArray(", t.vectorType.speciesName, ", ", name, ", 0)") + )); + } else if (argumentType instanceof VectorType.Shuffle t) { + arguments.add(new TestArgument( + List.of("int[] ", name, " = new int[1000];\n", + "LibraryRNG.fill(", name,");\n"), + name, + List.of("int[] ", name), + List.of("VectorShuffle.fromArray(", t.vectorType.speciesName, ", ", name, ", 0)") + )); + } else { + // We don't know anything special how to create different values + // each time, so let's just crate the same constant each time. + arguments.add(new TestArgument( + List.of(argumentType.name(), " ", name, " = ", argumentType.con(), ";\n"), + name, + List.of(argumentType.name(), " ", name), + name + )); + } + } + } + } + + // "join" together the arguments to make a comma separated list. + List passArguments = IntStream.range(0, arguments.size() * 2 - 1).mapToObj(i -> + (i % 2 == 0) ? arguments.get(i / 2).passMethodArgument() : ", " + ).toList(); + List receiveArguments = IntStream.range(0, arguments.size() * 2 - 1).mapToObj(i -> + (i % 2 == 0) ? arguments.get(i / 2).receiveMethodArgument() : ", " + ).toList(); + + List useArguments = arguments.stream().map(TestArgument::use).toList(); + + return scope( + """ + // --- $test start --- + // type: #type + + @Run(test = "$test") + public void $run() { + """, + arguments.stream().map(TestArgument::defineAndFill).toList(), + """ + Object v0 = $test( + """, + passArguments, + """ + ); + Object v1 = $reference( + """, + passArguments, + """ + ); + """, + expression.info.isResultDeterministic + ? "Verify.checkEQ(v0, v1);\n" + : "// result not deterministic - don't verify.\n", + """ + } + + @Test + public static Object $test( + """, + receiveArguments, + """ + ) { + """, + template2Body.asToken(expression, useArguments), + """ + } + + @DontCompile + public static Object $reference( + """, + receiveArguments, + """ + ) { + """, + template2Body.asToken(expression, useArguments), + """ + } + // --- $test end --- + """ + ); + }); + + for (VectorType.Vector type : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + tests.add(template1.asToken(type)); + tests.add(template2.asToken(type)); + } + + // Create the test class, which runs all tests. + return TestFrameworkClass.render( + // package and class name. + "compiler.vectorapi.templated", "Templated", + // Set of imports. + Set.of("compiler.lib.verify.*", + "java.util.Random", + "jdk.test.lib.Utils", + "compiler.lib.generators.*", + "jdk.incubator.vector.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); + } +} diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index 986cd044737..4240ba408b7 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java @@ -76,7 +76,7 @@ private static void test() throws Exception { Object lock = new Object(); opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); opts.addDockerOpts("--volume", sharedtmpdir.getAbsolutePath() + ":/tmp/"); - opts.addJavaOpts("-Xlog:os+container=trace", "-Xlog:perf+memops=debug", "-cp", "/test-classes/"); + opts.addJavaOpts("-Xlog:os+container=trace", "-Xlog:perf*=debug", "-cp", "/test-classes/"); Thread t1 = new Thread() { public void run() { @@ -115,12 +115,13 @@ public void run() { t1.join(); t2.join(); - Pattern pattern = Pattern.compile("perf,memops.*Trying to open (/tmp/hsperfdata_[a-z0-9]*/[0-9]*)"); + Pattern pattern = Pattern.compile("perf,memops.*Trying to open (/tmp/hsperfdata_[a-z0-9]*/(\\d+))"); Matcher matcher; matcher = pattern.matcher(out1.getStdout()); Asserts.assertTrue(matcher.find()); String file1 = matcher.group(1); + String pid1 = matcher.group(2); matcher = pattern.matcher(out2.getStdout()); Asserts.assertTrue(matcher.find()); @@ -134,8 +135,11 @@ public void run() { // have pid==1. // One of the two containers must fail to create the hsperf file. String s = "Cannot use file " + file1 + " because it is locked by another process"; + String s2 = "could not create file " + pid1 + ": existing file is not provably stale"; Asserts.assertTrue(out1.getStdout().contains(s) || - out2.getStdout().contains(s)); + out2.getStdout().contains(s) || + out1.getStdout().contains(s2) || + out2.getStdout().contains(s2)); } else { throw new SkippedException("Java in the two containers don't have the same pid: " + file1 + " vs " + file2); } diff --git a/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java b/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java new file mode 100644 index 00000000000..dc7e500ec9d --- /dev/null +++ b/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java @@ -0,0 +1,316 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package gc; + +/* + * @test id=parallel + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ParallelGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Parallel + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=g1 + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for G1GC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.G1 + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=shenandoah + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ShenandoahGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Shenandoah + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=genshen + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for generational ShenandoahGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Shenandoah + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=z + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ZGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Z + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * Nmethods have GC barriers and OOPs embedded into their code. GCs can patch nmethod's code + * which requires icache invalidation. Doing invalidation per instruction can be expensive. + * CPU can support hardware dcache and icache coherence. This would allow to defer cache + * invalidation. + * + * There are assertions for deferred cache invalidation. This test checks that all of them + * are passed. + */ + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; + +public class TestDeferredICacheInvalidation { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static class A { + public String s1; + public String s2; + public String s3; + public String s4; + public String s5; + public String s6; + public String s7; + public String s8; + public String s9; + } + + public static A a = new A(); + + private static int compLevel; + + public static class B { + public static void test0() { + } + + public static void test1() { + a.s1 = a.s1 + "1"; + } + + public static void test2() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + } + + public static void test3() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + } + + public static void test4() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + } + + public static void test5() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + } + + public static void test6() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + } + + public static void test7() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + } + + public static void test8() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + a.s8 = a.s8 + "8"; + } + + public static void test9() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + a.s8 = a.s8 + "8"; + a.s9 = a.s9 + "9"; + } + } + + private static void compileMethods() throws Exception { + for (var m : B.class.getDeclaredMethods()) { + if (!m.getName().startsWith("test")) { + continue; + } + m.invoke(null); + WB.markMethodProfiled(m); + WB.enqueueMethodForCompilation(m, compLevel); + while (WB.isMethodQueuedForCompilation(m)) { + Thread.sleep(100); + } + if (WB.getMethodCompilationLevel(m) != compLevel) { + throw new IllegalStateException("Method " + m + " is not compiled at the compilation level: " + compLevel + ". Got: " + WB.getMethodCompilationLevel(m)); + } + } + } + + public static void youngGC() throws Exception { + a = null; + WB.youngGC(); + } + + public static void fullGC() throws Exception { + // Thread synchronization + final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); + final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); + final AtomicBoolean running = new AtomicBoolean(true); + + // Thread 1: GC thread that runs for 1 second with 100ms sleep intervals + Thread gcThread = new Thread(() -> { + final long startTime = System.currentTimeMillis(); + final long duration = 1000; + try { + while (System.currentTimeMillis() - startTime < duration) { + writeLock.lock(); + try { + a = new A(); + WB.fullGC(); + } finally { + writeLock.unlock(); + } + Thread.sleep(100); + } + } catch (InterruptedException e) { + // Thread interrupted, exit + } + running.set(false); + }); + + // Threads 2-11: Test threads that execute test0() through test9() + Thread[] testThreads = new Thread[10]; + for (int i = 0; i < 10; i++) { + final int testIdx = i; + testThreads[i] = new Thread(() -> { + try { + var method = B.class.getDeclaredMethod("test" + testIdx); + while (running.get()) { + readLock.lock(); + try { + method.invoke(null); + } finally { + readLock.unlock(); + } + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(10); + } + }); + } + + // Start all threads + gcThread.start(); + for (Thread t : testThreads) { + t.start(); + } + + // Wait for all threads to complete + gcThread.join(); + for (Thread t : testThreads) { + t.join(); + } + } + + public static void main(String[] args) throws Exception { + if (!Boolean.TRUE.equals(WB.getBooleanVMFlag("UseSingleICacheInvalidation"))) { + throw new SkippedException("Skip. Test requires UseSingleICacheInvalidation enabled."); + } + compLevel = (args[1].equals("C1")) ? 1 : 4; + compileMethods(); + TestDeferredICacheInvalidation.class.getMethod(args[0]).invoke(null); + } +} diff --git a/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java b/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java index 3aec113990c..c4c1d364308 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1CompressedOops.java @@ -27,6 +27,7 @@ /* * @test TestG1CompressedOops * @bug 8354145 + * @requires vm.bits == 64 * @requires vm.flagless * @summary Verify that the flag UseCompressedOops is updated properly * @library /test/lib diff --git a/test/hotspot/jtreg/runtime/cds/BootAppendTests.java b/test/hotspot/jtreg/runtime/cds/BootAppendTests.java index 43466734fa3..642aa2209f4 100644 --- a/test/hotspot/jtreg/runtime/cds/BootAppendTests.java +++ b/test/hotspot/jtreg/runtime/cds/BootAppendTests.java @@ -162,7 +162,7 @@ public static void testBootAppendDuplicateModuleClass() throws Exception { .addSuffix("-Xlog:class+load=info", APP_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME); - String MATCH_PATTERN = ".class.load. javax.annotation.processing.FilerException source:.*bootAppend.jar*"; + String MATCH_PATTERN = ".class.load.*javax.annotation.processing.FilerException source:.*bootAppend.jar*"; CDSTestUtils.run(opts) .assertNormalExit(out -> { out.shouldNotMatch(MATCH_PATTERN); @@ -188,7 +188,7 @@ public static void testBootAppendExcludedModuleClass() throws Exception { APP_CLASS, BOOT_APPEND_MODULE_CLASS_NAME); CDSTestUtils.Result res = CDSTestUtils.run(opts); String MATCH_PATTERN = - ".class.load. javax.sound.sampled.MyClass source:.*bootAppend.jar*"; + ".class.load.*javax.sound.sampled.MyClass source:.*bootAppend.jar*"; if (mode.equals("on")) { res.assertSilentlyDisabledCDS(out -> { out.shouldHaveExitValue(0) @@ -225,7 +225,7 @@ public static void testBootAppendDuplicateExcludedModuleClass() throws Exception CDSTestUtils.Result res = CDSTestUtils.run(opts); String MATCH_PATTERN = - ".class.load. javax.annotation.processing.FilerException source:.*bootAppend.jar*"; + ".class.load.*javax.annotation.processing.FilerException source:.*bootAppend.jar*"; if (mode.equals("on")) { res.assertSilentlyDisabledCDS(out -> { out.shouldHaveExitValue(0) @@ -258,7 +258,7 @@ public static void testBootAppendClass() throws Exception { CDSTestUtils.Result res = CDSTestUtils.run(opts); String MATCH_PATTERN = - ".class.load. nonjdk.myPackage.MyClass source:.*bootAppend.jar*"; + ".class.load.*nonjdk.myPackage.MyClass source:.*bootAppend.jar*"; if (mode.equals("on")) { res.assertSilentlyDisabledCDS(out -> { out.shouldHaveExitValue(0) @@ -287,7 +287,7 @@ public static void testBootAppendExtraDir() throws Exception { CDSTestUtils.Result res = CDSTestUtils.run(opts); String MATCH_PATTERN = - ".class.load. nonjdk.myPackage.MyClass source:.*bootAppend.jar*"; + ".class.load.*nonjdk.myPackage.MyClass source:.*bootAppend.jar*"; if (mode.equals("on")) { res.assertSilentlyDisabledCDS(out -> { out.shouldHaveExitValue(0) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java b/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java index ed7f02569ef..8548f76d2ec 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/SpecifySysLoaderProp.java @@ -53,8 +53,10 @@ public static void main(String[] args) throws Exception { "-verbose:class", "-cp", appJar, "ReportMyLoader") - .assertNormalExit("[class,load] ReportMyLoader source: shared objects file", - "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@"); + .assertNormalExit(output -> { + output.shouldMatch(".class,load.*ReportMyLoader source: shared objects file"); + output.shouldContain("ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@"); + }); // (1) Try to execute the archive with -Djava.system.class.loader=no.such.Klass, // it should fail @@ -79,8 +81,8 @@ public static void main(String[] args) throws Exception { "TestClassLoader.called = true", //<-but this proves that TestClassLoader was indeed called. "TestClassLoader: loadClass(\"ReportMyLoader\",") //<- this also proves that TestClassLoader was indeed called. .assertNormalExit(output -> { - output.shouldMatch(".class,load. TestClassLoader source: file:"); - output.shouldMatch(".class,load. ReportMyLoader source: file:.*" + jarFileName); + output.shouldMatch(".class,load.*TestClassLoader source: file:"); + output.shouldMatch(".class,load.*ReportMyLoader source: file:.*" + jarFileName); output.shouldContain("full module graph: disabled due to incompatible property: java.system.class.loader="); }); @@ -91,9 +93,11 @@ public static void main(String[] args) throws Exception { "-verbose:class", "-cp", appJar, "TrySwitchMyLoader") - .assertNormalExit("[class,load] ReportMyLoader source: shared objects file", - "TrySwitchMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", - "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", - "TestClassLoader.called = false"); + .assertNormalExit(output -> { + output.shouldMatch(".class,load.*ReportMyLoader source: shared objects file"); + output.shouldContain("TrySwitchMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@"); + output.shouldContain("ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@"); + output.shouldContain("TestClassLoader.called = false"); + }); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java index 1a8313a3058..9fef0845d1d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java @@ -31,7 +31,10 @@ * @build ReturnIntegerAsString * @build AOTCacheSupportForCustomLoaders * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AppWithCustomLoaders AppWithCustomLoaders$MyLoader - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar AppWithCustomLoaders$MyLoadeeA AppWithCustomLoaders$MyLoadeeB ReturnIntegerAsString + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * AppWithCustomLoaders$MyLoadeeA AppWithCustomLoaders$MyLoadeeB + * AppWithCustomLoaders$MyLoadeeC AppWithCustomLoaders$MyLoadeeD + * ReturnIntegerAsString * @run driver AOTCacheSupportForCustomLoaders AOT */ @@ -40,6 +43,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.io.File; +import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; @@ -62,10 +66,17 @@ public static void main(String... args) throws Exception { "--module-path=" + modulePath, "--add-modules=com.test") .appCommandLine("AppWithCustomLoaders", modulePath) + .setTrainingChecker((OutputAnalyzer out) -> { + out.shouldContain("Skipping AppWithCustomLoaders$MyLoadeeC: Not loaded from \"file:\" code source") + .shouldContain("Skipping AppWithCustomLoaders$MyLoadeeD: super AppWithCustomLoaders$MyLoadeeC is excluded") + .shouldContain("Skipping ReturnIntegerAsString: Failed verification"); + }) .setAssemblyChecker((OutputAnalyzer out) -> { out.shouldMatch(",class.*unreg AppWithCustomLoaders[$]MyLoadeeA") .shouldMatch(",class.*unreg com.test.Foo") .shouldMatch(",class.*array \\[LAppWithCustomLoaders[$]MyLoadeeA;") + .shouldNotMatch("class.*unreg.*MyLoadeeC") // not from "file:" code source + .shouldNotMatch("class.*unreg.*MyLoadeeD") // parent is not from "file:" code source .shouldNotMatch(",class.* ReturnIntegerAsString"); }) .setProductionChecker((OutputAnalyzer out) -> { @@ -76,7 +87,7 @@ public static void main(String... args) throws Exception { } class AppWithCustomLoaders { - static MyLoader loader; // keep alive so its classes can be cached. + static MyLoader loader; // keep alive public static void main(String args[]) throws Exception { File custJar = new File("cust.jar"); @@ -86,6 +97,7 @@ public static void main(String args[]) throws Exception { test1(loader); test2(loader); test3(args[0]); + test4(loader); // TODO: more test cases JDK-8354557 } @@ -147,10 +159,37 @@ static void test3(String modulePath) throws Exception { } } + // Test 4: classes that don't use file: code source should be excluded + static void test4(MyLoader loader) throws Exception { + Class c = loader.loadLoadeeC(); + Class d = loader.loadClass("AppWithCustomLoaders$MyLoadeeD"); + + URL ccs = c.getProtectionDomain().getCodeSource().getLocation(); + + if (ccs != null) { + throw new RuntimeException("MyLoadeeC should have null CodeSource but got: " + ccs); + } + + if (d.getSuperclass() != c) { + throw new RuntimeException("MyLoadeeC should be super class of MyLoadeeD"); + } + } + public static class MyLoader extends URLClassLoader { public MyLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } + + public Class loadLoadeeC() throws Exception { + try (InputStream in = getResourceAsStream("AppWithCustomLoaders$MyLoadeeC.class")) { + byte[] b = in.readAllBytes(); + // Define MyLoadeeC without specifying a ProtectionDomain. As a result, this + // class will get an empty ProtectionDomain whose CodeSource location will be null. + // + // This class should be excluded from the AOT cache. See JDK-8380291 + return defineClass(b, 0, b.length); + } + } } public static class MyLoadeeA { @@ -180,4 +219,9 @@ static void test() { } public static class MyLoadeeB extends MyLoadeeA {} + + + public static class MyLoadeeC {} + + public static class MyLoadeeD extends MyLoadeeC {} } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java index 09290960d2f..3b9458c14da 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.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 @@ -201,12 +201,18 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception if (aotCacheShift == -1 || currentShift == -1 || aotCacheBase == -1 || currentBase == -1) { throw new RuntimeException("Failed to find CompressedOops settings"); } + + // Changes in compressed oop encoding could randomly affect flags like AllocatePrefetchDistance + // due to the OS-assigned range of the Java heap. If that happens, the exact error message may vary + String disabledMsg = "AOT Code Cache disabled:"; if (aotCacheShift != currentShift) { - out.shouldContain("AOT Code Cache disabled: it was created with different CompressedOops::shift()"); + out.shouldContain(disabledMsg); } else if ((aotCacheBase == 0 || currentBase == 0) && (aotCacheBase != currentBase)) { - out.shouldContain("AOTStubCaching is disabled: incompatible CompressedOops::base()"); + out.shouldContain(disabledMsg); } else { - out.shouldMatch("Read \\d+ entries table at offset \\d+ from AOT Code Cache"); + if (!out.contains(disabledMsg)) { + out.shouldMatch("Read \\d+ entries table at offset \\d+ from AOT Code Cache"); + } } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java index 761c2c5382d..d3c6a23efa0 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeFlags.java @@ -23,7 +23,8 @@ */ /** - * @test + * @test id=default_gc + * @requires vm.gc != "Z" * @summary Sanity test of combinations of the AOT Code Caching diagnostic flags * @requires vm.cds.supports.aot.code.caching * @requires vm.compiler1.enabled & vm.compiler2.enabled @@ -38,7 +39,64 @@ * JavacBenchApp$ClassFile * JavacBenchApp$FileManager * JavacBenchApp$SourceFile - * @run driver AOTCodeFlags + * @run driver/timeout=1500 AOTCodeFlags + */ +/** + * @test id=Z + * @requires vm.gc.Z + * @summary Sanity test of combinations of the AOT Code Caching diagnostic flags + * @requires vm.cds.supports.aot.code.caching + * @requires vm.compiler1.enabled & vm.compiler2.enabled + * @comment Both C1 and C2 JIT compilers are required because the test verifies + * compiler's runtime blobs generation. + * @requires vm.opt.VerifyOops == null | vm.opt.VerifyOops == false + * @comment VerifyOops flag switch off AOT code generation. Skip it. + * @library /test/lib /test/setup_aot + * @build AOTCodeFlags JavacBenchApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * JavacBenchApp + * JavacBenchApp$ClassFile + * JavacBenchApp$FileManager + * JavacBenchApp$SourceFile + * @run driver/timeout=1500 AOTCodeFlags Z + */ +/** + * @test id=shenandoah + * @requires vm.gc.Shenandoah + * @summary Sanity test of combinations of the AOT Code Caching diagnostic flags + * @requires vm.cds.supports.aot.code.caching + * @requires vm.compiler1.enabled & vm.compiler2.enabled + * @comment Both C1 and C2 JIT compilers are required because the test verifies + * compiler's runtime blobs generation. + * @requires vm.opt.VerifyOops == null | vm.opt.VerifyOops == false + * @comment VerifyOops flag switch off AOT code generation. Skip it. + * @library /test/lib /test/setup_aot + * @build AOTCodeFlags JavacBenchApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * JavacBenchApp + * JavacBenchApp$ClassFile + * JavacBenchApp$FileManager + * JavacBenchApp$SourceFile + * @run driver/timeout=1500 AOTCodeFlags Shenandoah + */ +/** + * @test id=parallel + * @requires vm.gc.Parallel + * @summary Sanity test of combinations of the AOT Code Caching diagnostic flags + * @requires vm.cds.supports.aot.code.caching + * @requires vm.compiler1.enabled & vm.compiler2.enabled + * @comment Both C1 and C2 JIT compilers are required because the test verifies + * compiler's runtime blobs generation. + * @requires vm.opt.VerifyOops == null | vm.opt.VerifyOops == false + * @comment VerifyOops flag switch off AOT code generation. Skip it. + * @library /test/lib /test/setup_aot + * @build AOTCodeFlags JavacBenchApp + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * JavacBenchApp + * JavacBenchApp$ClassFile + * JavacBenchApp$FileManager + * JavacBenchApp$SourceFile + * @run driver/timeout=1500 AOTCodeFlags Parallel */ import java.util.ArrayList; @@ -48,20 +106,23 @@ import jdk.test.lib.process.OutputAnalyzer; public class AOTCodeFlags { + private static String gcName = null; public static void main(String... args) throws Exception { - Tester t = new Tester(); + Tester t = new Tester(args.length == 0 ? null : args[0]); // Run only 2 modes (0 - no AOT code, 1 - AOT adapters) until JDK-8357398 is fixed - for (int mode = 0; mode < 2; mode++) { + for (int mode = 0; mode < 4; mode++) { t.setTestMode(mode); t.run(new String[] {"AOT", "--two-step-training"}); } } static class Tester extends CDSAppTester { private int testMode; + private String gcName; - public Tester() { + public Tester(String name) { super("AOTCodeFlags"); testMode = 0; + gcName = name; } boolean isAdapterCachingOn() { @@ -84,6 +145,24 @@ public List getVMArgsForTestMode() { return list; } + public List getGCArgs() { + List args = new ArrayList(); + args.add("-Xmx100M"); + if (gcName == null) { + return args; + } + switch (gcName) { + case "G1": + case "Z": + case "Shenandoah": + case "Parallel": + args.add("-XX:+Use" + gcName + "GC"); + return args; + default: + throw new RuntimeException("Unexpected GC name " + gcName); + } + } + @Override public String classpath(RunMode runMode) { return "app.jar"; @@ -97,10 +176,12 @@ public String[] vmArgs(RunMode runMode) { List args = getVMArgsForTestMode(); args.addAll(List.of("-Xlog:aot+codecache+init=debug", "-Xlog:aot+codecache+exit=debug")); + args.addAll(getGCArgs()); return args.toArray(new String[0]); } } - return new String[] {}; + List args = getGCArgs(); + return args.toArray(new String[args.size()]); } @Override @@ -147,7 +228,10 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception // AOTStubCaching is on, non-zero stubs should be stored/loaded out.shouldMatch("Shared Blobs:\\s+total=[1-9][0-9]+"); out.shouldMatch("C1 Blobs:\\s+total=[1-9][0-9]+"); - out.shouldMatch("C2 Blobs:\\s+total=[1-9][0-9]+"); + // we do not currently load or store C2 stubs + // because we are seeing weird memory errors + // when loading them -- see JDK-8357593 + out.shouldMatch("C2 Blobs:\\s+total=0"); break; } } else { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java index 19ab392c036..d9e1b15602c 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.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 @@ -91,7 +91,7 @@ public String[] appCommandLine(RunMode runMode) { public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { out.shouldHaveExitValue(0); if (runMode.isProductionRun()) { - out.shouldMatch(".class.load. test.java.lang.invoke." + testClassName + + out.shouldMatch(".class.load.* test.java.lang.invoke." + testClassName + "[$][$]Lambda.*/0x.*source:.*shared.*objects.*file"); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index 4bec633553b..8e87985e3d3 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,8 @@ /* * @test - * @bug 8359412 8370922 + * @bug 8359412 8370922 8369699 + * @key randomness * @summary Demonstrate the use of Expressions from the Template Library. * @modules java.base/jdk.internal.misc * @modules jdk.incubator.vector @@ -37,6 +38,8 @@ import java.util.List; import java.util.ArrayList; import java.util.Set; +import java.util.Random; +import jdk.test.lib.Utils; import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; @@ -48,6 +51,8 @@ import compiler.lib.template_framework.library.TestFrameworkClass; public class TestExpressions { + private static final Random RANDOM = Utils.getRandomInstance(); + public static void main(String[] args) { // Create a new CompileFramework instance. CompileFramework comp = new CompileFramework(); @@ -126,16 +131,26 @@ public static String generate(CompileFramework comp) { ); }); + // The scalar operations are very important and we would like to always test them all. for (Expression operation : Operations.SCALAR_NUMERIC_OPERATIONS) { tests.add(withConstantsTemplate.asToken(operation)); } + // There are a LOT of instructions, especially a lot of vector instructions. + // A bit too many to run them all in an individual test. + // So let's just sample some at random. + for (int i = 0; i < 100; i++) { + int r = RANDOM.nextInt(Operations.ALL_OPERATIONS.size()); + Expression operation = Operations.ALL_OPERATIONS.get(r); + tests.add(withConstantsTemplate.asToken(operation)); + } + // Create the test class, which runs all tests. return TestFrameworkClass.render( // package and class name. "p.xyz", "InnerTest", // Set of imports. - Set.of("compiler.lib.verify.*", "jdk.incubator.vector.Float16"), + Set.of("compiler.lib.verify.*", "jdk.incubator.vector.*"), // classpath, so the Test VM has access to the compiled class files. comp.getEscapedClassPathOfCompiledClasses(), // The list of tests. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index facaefaa8f3..861298c7da7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -254,6 +254,18 @@ public static void test_abbrev() { } if (allSame) { throw new RuntimeException("all values were the same for #type"); } } + { + #type[] a = new #type[1_000]; + LibraryRNG.fill(a); + boolean allSame = true; + for (int i = 0; i < a.length; i++) { + if (a[i] != a[0]) { + allSame = false; + break; + } + } + if (allSame) { throw new RuntimeException("all values were the same for #type"); } + } """ )); diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestVectorTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestVectorTypes.java new file mode 100644 index 00000000000..554cdc1094c --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestVectorTypes.java @@ -0,0 +1,134 @@ +/* + * 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 8358772 + * @summary Demonstrate the use of VectorTypes (Vector API) form the Template Library. + * @modules java.base/jdk.internal.misc + * @modules jdk.incubator.vector + * @library /test/lib / + * @compile ../../../compiler/lib/verify/Verify.java + * @run main ${test.main.class} + */ + +package template_framework.examples; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.scope; +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; + +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.VectorType; +import compiler.lib.template_framework.library.TestFrameworkClass; + +/** + * This test shows the use of {@link VectorType}. + */ +public class TestVectorTypes { + + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); + + // Compile the source file. + comp.compile("--add-modules=jdk.incubator.vector"); + + // p.xyz.InnerTest.main(); + comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] { + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED" + }}); + } + + // Generate a Java source file as String + public static String generate(CompileFramework comp) { + // Generate a list of test methods. + List tests = new ArrayList<>(); + + var vectorTemplate = Template.make("type", (VectorType.Vector type) -> { + return scope( + let("elementType", type.elementType), + let("length", type.length), + let("SPECIES", type.speciesName), + let("maskType", type.maskType), + let("shuffleType", type.shuffleType), + """ + // #type #elementType #length #SPECIES + @Test + public static void $test() { + """, + " #elementType scalar = ", type.elementType.con(), ";\n", + " #maskType mask = ", type.maskType.con(), ";\n", + " #shuffleType shuffle = ", type.shuffleType.con(), ";\n", + """ + #type zeros = #{type}.zero(#SPECIES); + #type zeros2 = #{type}.broadcast(#SPECIES, 0); + #type vector = #{type}.broadcast(#SPECIES, scalar); + Verify.checkEQ(zeros, zeros2); + Verify.checkEQ(zeros.add(vector), vector.add(zeros)); + Verify.checkEQ(vector.length(), #length); + Verify.checkEQ(vector.lane(#length-1), scalar); + + #type v1 = zeros.blend(vector, mask); + #type v2 = vector.blend(zeros, mask.not()); + Verify.checkEQ(v1, v2); + + #type iota = zeros.addIndex(1); + #type v3 = iota.rearrange(iota.toShuffle()); + Verify.checkEQ(iota, v3); + #type v4 = iota.rearrange(shuffle); + Verify.checkEQ(shuffle.wrapIndexes(), v4.toShuffle()); + Verify.checkEQ(shuffle.wrapIndexes().toVector(), v4); + } + """ + ); + }); + + for (var type : CodeGenerationDataNameType.VECTOR_VECTOR_TYPES) { + tests.add(vectorTemplate.asToken(type)); + } + + // Create the test class, which runs all tests. + return TestFrameworkClass.render( + // package and class name. + "p.xyz", "InnerTest", + // Set of imports. + Set.of("compiler.lib.verify.*", "jdk.incubator.vector.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); + } +} diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/AttachProvider/AttachProvider01/AttachProvider01.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/AttachProvider/AttachProvider01/AttachProvider01.java index 7e3f7a9687d..dbe640a8719 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/AttachProvider/AttachProvider01/AttachProvider01.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/AttachProvider/AttachProvider01/AttachProvider01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -36,8 +36,7 @@ * @library /vmTestbase /test/hotspot/jtreg/vmTestbase * /test/lib * @build nsk.share.aod.DummyTargetApplication - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.AttachProvider.AttachProvider01.AttachProvider01 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine01/VirtualMachine01.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine01/VirtualMachine01.java index 257d4022d2b..3f171367361 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine01/VirtualMachine01.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine01/VirtualMachine01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -40,8 +40,7 @@ * @library /vmTestbase /test/hotspot/jtreg/vmTestbase * /test/lib * @build nsk.share.aod.DummyTargetApplication - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.VirtualMachine.VirtualMachine01.VirtualMachine01 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine04/VirtualMachine04.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine04/VirtualMachine04.java index 3bd98cd5043..066b2ef8a0f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine04/VirtualMachine04.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine04/VirtualMachine04.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -39,8 +39,7 @@ * @library /vmTestbase /test/hotspot/jtreg/vmTestbase * /test/lib * @build nsk.aod.VirtualMachine.VirtualMachine04.VM04Target - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.VirtualMachine.VirtualMachine04.VirtualMachine04 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine05/VirtualMachine05.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine05/VirtualMachine05.java index 3c7e90a7ffa..c8e7299fd39 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine05/VirtualMachine05.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine05/VirtualMachine05.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -36,8 +36,7 @@ * @library /vmTestbase /test/hotspot/jtreg/vmTestbase * /test/lib * @build nsk.share.aod.DummyTargetApplication - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.VirtualMachine.VirtualMachine05.VirtualMachine05 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine06/VirtualMachine06.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine06/VirtualMachine06.java index 0fd11e6b8d5..f9bf43d2fe0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine06/VirtualMachine06.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine06/VirtualMachine06.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -76,8 +76,7 @@ * * * @build nsk.share.aod.TargetApplicationWaitingAgents - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.VirtualMachine.VirtualMachine06.VirtualMachine06 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine07/VirtualMachine07.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine07/VirtualMachine07.java index 73b4522ae6e..ab37e253fc8 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine07/VirtualMachine07.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine07/VirtualMachine07.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -42,7 +42,6 @@ * /test/lib * @build nsk.share.aod.TargetApplicationWaitingAgents * @run main/othervm/native - * -XX:+UsePerfData * nsk.aod.VirtualMachine.VirtualMachine07.VirtualMachine07 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine08/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine08/TestDescription.java index e484641dd2a..14a05f53702 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine08/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine08/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,6 @@ * /test/lib * @build nsk.share.aod.TargetApplicationWaitingAgents * @run main/othervm/native - * -XX:+UsePerfData * nsk.aod.VirtualMachine.VirtualMachine07.VirtualMachine07 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine09/VirtualMachine09.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine09/VirtualMachine09.java index 47d1a718e88..3b2852fc0ad 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine09/VirtualMachine09.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine09/VirtualMachine09.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -39,7 +39,6 @@ * /test/lib * @build nsk.aod.VirtualMachine.VirtualMachine09.VM09Target * @run main/othervm/native - * -XX:+UsePerfData * nsk.aod.VirtualMachine.VirtualMachine09.VirtualMachine09 * -jdk ${test.jdk} * -javaOpts="-agentlib:VirtualMachine09agent00 -XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine10/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine10/TestDescription.java index c9c5b1ee64f..139fe236635 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine10/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachine/VirtualMachine10/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ * /test/lib * @build nsk.aod.VirtualMachine.VirtualMachine09.VM09Target * @run main/othervm/native - * -XX:+UsePerfData * nsk.aod.VirtualMachine.VirtualMachine09.VirtualMachine09 * -jdk ${test.jdk} * -javaOpts="-agentlib:VirtualMachine09agent00 -XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachineDescriptor/VirtualMachineDescriptor01/VirtualMachineDescriptor01.java b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachineDescriptor/VirtualMachineDescriptor01/VirtualMachineDescriptor01.java index 47f837a110f..02448528f12 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachineDescriptor/VirtualMachineDescriptor01/VirtualMachineDescriptor01.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/aod/VirtualMachineDescriptor/VirtualMachineDescriptor01/VirtualMachineDescriptor01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, 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 @@ -38,8 +38,7 @@ * @library /vmTestbase /test/hotspot/jtreg/vmTestbase * /test/lib * @build nsk.share.aod.DummyTargetApplication - * @run main/othervm - * -XX:+UsePerfData + * @run driver * nsk.aod.VirtualMachineDescriptor.VirtualMachineDescriptor01.VirtualMachineDescriptor01 * -jdk ${test.jdk} * -javaOpts="-XX:+UsePerfData ${test.vm.opts} ${test.java.opts}" diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java index 9899c762a7a..abfca89ca4d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/aod/AODTestRunner.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 @@ -103,9 +103,14 @@ protected void runTest() { /* * Create target application id required by the Utils.findVMIdUsingJPS */ + String classPath = System.getProperty("test.class.path"); + if (classPath == null) { + throw new RuntimeException("test.class.path is not set"); + } String targetAppCmd = // path to java argParser.getTestedJDK() + File.separator + "bin" + File.separator + "java " + + "-classpath " + classPath + " " + // VM property to identify VM running target application "-D" + appIdProperty + "=" + targetAppId + " " + // VM opts diff --git a/test/hotspot/jtreg/vmTestbase/vm/mlvm/mixed/stress/java/findDeadlock/TestDescription.java b/test/hotspot/jtreg/vmTestbase/vm/mlvm/mixed/stress/java/findDeadlock/TestDescription.java index ec2b2753340..ed186aa3f84 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/mlvm/mixed/stress/java/findDeadlock/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/vm/mlvm/mixed/stress/java/findDeadlock/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,8 @@ * @library /vmTestbase * /test/lib * + * @requires vm.compMode != "Xcomp" + * * @comment build test class and indify classes * @build vm.mlvm.mixed.stress.java.findDeadlock.INDIFY_Test * @run driver vm.mlvm.share.IndifiedClassesBuilder diff --git a/test/jdk/ProblemList-coh.txt b/test/jdk/ProblemList-coh.txt index b3bddb0c9f4..e69de29bb2d 100644 --- a/test/jdk/ProblemList-coh.txt +++ b/test/jdk/ProblemList-coh.txt @@ -1,41 +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. -# - -############################################################################# -# -# List of quarantined tests for testing with -XX:+UseCompactObjectHeaders -# -############################################################################# - -############################################################################# - -# Preview project specific failures go here at the end of the file. -# -# These are NOT failures that occur with the '--enable-preview' option -# specified; those go in the appropriate ProblemList-enable-preview.txt file. -# These are failures that occur WITHOUT the '--enable-preview' option -# specified AND occur because of some issue with preview project code, -# in either implementation or test code. - -############################################################################# - diff --git a/test/jdk/com/sun/jdi/JdbReadTwiceTest.sh b/test/jdk/com/sun/jdi/JdbReadTwiceTest.sh index 5902abe50ad..8103fb6f01d 100644 --- a/test/jdk/com/sun/jdi/JdbReadTwiceTest.sh +++ b/test/jdk/com/sun/jdi/JdbReadTwiceTest.sh @@ -68,6 +68,12 @@ jdbFiles="$HOME/jdb.ini $HOME/.jdbrc $here/jdb.ini $here/.jdbrc $tmpResult $fred cd $here failed= +target_os=$($TESTJAVA/bin/java -XshowSettings:all 2>&1 | grep 'os.name' | grep -i 'windows') +if [ -n "$target_os" ]; then + IS_WINDOWS=true +else + IS_WINDOWS=false +fi mkFiles() { @@ -84,7 +90,7 @@ failIfNot() # $1 is the expected number of occurances of $2 in the jdb output. count=$1 shift - if [ -r c:/ ] ; then + if [ "${IS_WINDOWS}" = "true" ] ; then sed -e 's@\\@/@g' $tmpResult > $tmpResult.1 mv $tmpResult.1 $tmpResult fi @@ -177,7 +183,7 @@ mkFiles $HOME/.jdbrc $here/jdb.ini clean -if [ ! -r c:/ ] ; then +if [ "${IS_WINDOWS}" != "true" ] ; then # No symlinks on windows. echo echo "+++++++++++++++++++++++++++++++++++" @@ -191,7 +197,7 @@ if [ ! -r c:/ ] ; then fi -if [ ! -r c:/ ] ; then +if [ "${IS_WINDOWS}" != "true" ] ; then # No symlinks on windows. echo echo "+++++++++++++++++++++++++++++++++++" diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal1Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal1Test.java index 7f2ae2361ae..7ee265cf34f 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal1Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal2Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal2Test.java index 5fef862611d..53a7d002a49 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal2Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal3Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal3Test.java index d054050faba..9718a05e53b 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal3Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal4Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal4Test.java index c3dea7cd590..7ae3898637e 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal4Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal4Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal5Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal5Test.java index 503c2f1cd01..026baab2229 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal5Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal5Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal6Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal6Test.java index 5fbbf040c58..0a93c77a7fa 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal6Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogAppModal6Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDWDTest.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDWDTest.java index 045c7e5ad49..5424744ad43 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDWDTest.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDWDTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,11 @@ */ -import java.awt.*; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; // DWD: Dialog, Window, Dialog diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal1Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal1Test.java index c115fde948b..1f5871caf6d 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal1Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal2Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal2Test.java index 604d3d41f1b..3b4092d3b01 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal2Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal3Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal3Test.java index b0a12d43cd4..d59cbd31396 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal3Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal4Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal4Test.java index 4a392c399db..6a4a967672c 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal4Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal4Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal5Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal5Test.java index 8eacf51cd5e..6bcb7fa6c54 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal5Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal5Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal6Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal6Test.java index 0d50231522c..aff84c3b40f 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal6Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal6Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal7Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal7Test.java index cde4592be91..9fd7d72a399 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal7Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogDocModal7Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -32,6 +32,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogFWDTest.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogFWDTest.java index 0026c0da50f..e77024d68e2 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogFWDTest.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogFWDTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, 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 @@ -21,8 +21,11 @@ * questions. */ -import java.awt.*; - +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; // FWD: Frame, Window, Dialog diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal1Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal1Test.java index cd845cf3a75..b7080d2081b 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal1Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal2Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal2Test.java index 5fe63428bb7..349c5900a41 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal2Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal3Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal3Test.java index 37da58a4bfe..911a2fb068a 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal3Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal4Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal4Test.java index 2761a842c86..431e2302215 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal4Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal4Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal5Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal5Test.java index d8e29db02b7..29ad342b71a 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal5Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal5Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal6Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal6Test.java index d70bd001ad1..88c7dd5e29e 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModal6Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModal6Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogModalityTest.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogModalityTest.java index 7f69d6176d0..00a6a58bd73 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogModalityTest.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogModalityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, 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 @@ -21,7 +21,11 @@ * questions. */ -import java.awt.*; +import java.awt.Dialog; +import java.awt.EventQueue; +import java.awt.FileDialog; +import java.awt.Frame; +import java.awt.Toolkit; public class FileDialogModalityTest { diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal1Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal1Test.java index a02860c25fb..e6463081a81 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal1Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal2Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal2Test.java index 47667d4b7ba..0fff3b3c43f 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal2Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal3Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal3Test.java index 6d3354a7472..31c1f42ca93 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal3Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal4Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal4Test.java index 1854dd367c5..0050d57839b 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal4Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal4Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal5Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal5Test.java index 9425fe5288d..c05af4c399e 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal5Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal5Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal6Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal6Test.java index 70ec49a038c..98cb842e65c 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal6Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal6Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal7Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal7Test.java index c34e9201f40..7c29610e205 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal7Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogNonModal7Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -32,6 +32,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal1Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal1Test.java index 7278a9db0a1..8578a69bed4 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal1Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal1Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal2Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal2Test.java index 282c84f64dc..182ebfd43ec 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal2Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal2Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal3Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal3Test.java index e09227564ef..78344558b40 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal3Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal3Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal4Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal4Test.java index d23e781a3c8..3157ebe9a2c 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal4Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal4Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal5Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal5Test.java index 515bd0072c2..11d10557006 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal5Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal5Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal6Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal6Test.java index 725924ca1c5..4ef7699fb0b 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal6Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal6Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -35,6 +35,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal7Test.java b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal7Test.java index f6e5bb80a38..bb416493a36 100644 --- a/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal7Test.java +++ b/test/jdk/java/awt/Modal/FileDialog/FileDialogTKModal7Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -32,6 +32,7 @@ * @library ../helpers /lib/client/ * @library /test/lib * @build ExtendedRobot + * @build jdk.test.lib.Asserts * @build Flag * @build TestDialog * @build TestFrame diff --git a/test/jdk/java/awt/Modal/helpers/TestDialog.java b/test/jdk/java/awt/Modal/helpers/TestDialog.java index 2ae93f835b9..a56ce1f5164 100644 --- a/test/jdk/java/awt/Modal/helpers/TestDialog.java +++ b/test/jdk/java/awt/Modal/helpers/TestDialog.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 @@ -22,11 +22,23 @@ */ -import java.awt.*; -import java.awt.event.*; - -import static jdk.test.lib.Asserts.*; - +import java.awt.Button; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; + +import static jdk.test.lib.Asserts.assertEQ; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; public class TestDialog extends Dialog implements ActionListener, diff --git a/test/jdk/java/awt/Modal/helpers/TestFrame.java b/test/jdk/java/awt/Modal/helpers/TestFrame.java index dd8ed1530ef..d002d6faf8e 100644 --- a/test/jdk/java/awt/Modal/helpers/TestFrame.java +++ b/test/jdk/java/awt/Modal/helpers/TestFrame.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 @@ -21,11 +21,22 @@ * questions. */ -import java.awt.*; -import java.awt.event.*; - -import static jdk.test.lib.Asserts.*; - +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; + +import static jdk.test.lib.Asserts.assertEQ; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; public class TestFrame extends Frame implements ActionListener, diff --git a/test/jdk/java/awt/Modal/helpers/TestWindow.java b/test/jdk/java/awt/Modal/helpers/TestWindow.java index 1e2ffb078c8..249f56ead7d 100644 --- a/test/jdk/java/awt/Modal/helpers/TestWindow.java +++ b/test/jdk/java/awt/Modal/helpers/TestWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, 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 @@ -21,11 +21,23 @@ * questions. */ -import java.awt.*; -import java.awt.event.*; - -import static jdk.test.lib.Asserts.*; - +import java.awt.Button; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Panel; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; + +import static jdk.test.lib.Asserts.assertEQ; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; public class TestWindow extends Window implements ActionListener, diff --git a/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java b/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java index 6bff8d5e592..0f5acf607ba 100644 --- a/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java +++ b/test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java @@ -37,12 +37,11 @@ import java.nio.charset.UnsupportedCharsetException; import java.util.stream.Stream; -import jtreg.SkippedException; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentest4j.TestAbortedException; /* * @test @@ -180,7 +179,7 @@ void testCase(String encoding) throws IOException { cs = Charset.forName(encoding); System.out.println("Charset: " + cs); } catch (UnsupportedCharsetException use) { - throw new SkippedException("Charset not supported: " + encoding); + throw new TestAbortedException("Charset not supported: " + encoding); } String cleanCSName = cleanCharsetName(cs); diff --git a/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java index 123cccd53e7..c7185f691b2 100644 --- a/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java +++ b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassFile; +import java.lang.classfile.CodeBuilder; import java.lang.classfile.TypeKind; import java.lang.constant.*; @@ -32,6 +33,7 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import static java.lang.invoke.MethodType.fromMethodDescriptorString; @@ -135,4 +137,25 @@ public static ClassDesc classDesc(Class c, String suffix) { String classDescStr = sb.insert(sb.length() - 1, suffix).toString(); return ClassDesc.ofDescriptor(classDescStr); } + + public static MethodHandle buildMethodHandle(MethodHandles.Lookup l, String methodName, MethodType methodType, Consumer builder) { + ClassDesc genClassDesc = classDesc(l.lookupClass(), "$Code_" + COUNT.getAndIncrement()); + return buildMethodHandle(l, genClassDesc, methodName, methodType, builder); + } + + private static MethodHandle buildMethodHandle(MethodHandles.Lookup l, ClassDesc classDesc, String methodName, MethodType methodType, Consumer builder) { + try { + byte[] bytes = ClassFile.of().build(classDesc, classBuilder -> { + classBuilder.withMethod(methodName, + MethodTypeDesc.ofDescriptor(methodType.toMethodDescriptorString()), + ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, + methodBuilder -> methodBuilder.withCode(builder)); + }); + Class clazz = l.defineClass(bytes); + return l.findStatic(clazz, methodName, methodType); + } catch (Throwable t) { + t.printStackTrace(); + throw new RuntimeException("Failed to buildMethodHandle: " + methodName + " type " + methodType); + } + } } diff --git a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java index 86936ae41d0..36eae0b5c32 100644 --- a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java +++ b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java @@ -88,7 +88,6 @@ import jdk.test.lib.RandomFactory; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; -import jtreg.SkippedException; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; @@ -102,9 +101,11 @@ import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.nio.channels.DatagramChannel; +import java.util.Optional; import java.util.Random; import static java.net.StandardSocketOptions.SO_RCVBUF; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.expectThrows; @@ -115,8 +116,8 @@ public class SendReceiveMaxSize { private int BUF_LIMIT; private InetAddress HOST_ADDR; - private final static int IPV4_SNDBUF = 65507; - private final static int IPV6_SNDBUF = 65527; + private final static int IPV4_SNDBUF = IPSupport.getMaxUDPSendBufSizeIPv4(); + private final static int IPV6_SNDBUF = IPSupport.getMaxUDPSendBufSizeIPv6(); private final static Class IOE = IOException.class; private final static Random random = RandomFactory.getRandom(); @@ -127,14 +128,11 @@ public interface DatagramSocketSupplier { @BeforeTest public void setUp() throws IOException { - try { - // This method throws jtreg.SkippedException, which is - // interpreted as a test failure by testng - IPSupport.throwSkippedExceptionIfNonOperational(); - } catch (SkippedException skip) { - // throws the appropriate TestNG SkipException - throw new SkipException(skip.getMessage(), skip); - } + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); + HOST_ADDR = PREFER_LOOPBACK ? InetAddress.getLoopbackAddress() : InetAddress.getLocalHost(); BUF_LIMIT = (HOST_ADDR instanceof Inet6Address) ? IPV6_SNDBUF : IPV4_SNDBUF; System.out.printf("Host address: %s, Buffer limit: %d%n", HOST_ADDR, BUF_LIMIT); diff --git a/test/jdk/java/net/DatagramSocketImpl/TestCreate.java b/test/jdk/java/net/DatagramSocketImpl/TestCreate.java index 81772074ae2..8d526cf0530 100644 --- a/test/jdk/java/net/DatagramSocketImpl/TestCreate.java +++ b/test/jdk/java/net/DatagramSocketImpl/TestCreate.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 @@ -25,10 +25,9 @@ * @test * @bug 8238231 * @summary test that DatagramSocket calls java.net.DatagramSocketImpl::create - * @run testng/othervm TestCreate + * @run junit/othervm ${test.main.class} */ -import org.testng.annotations.Test; import java.io.IOException; import java.net.DatagramPacket; @@ -44,7 +43,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TestCreate { diff --git a/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java b/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java index 2e223f1820c..c4e88b6a2c0 100644 --- a/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java +++ b/test/jdk/java/net/DatagramSocketImpl/TestDefaultBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 8224477 * @summary Basic test for java.net.DatagramSocketImpl default behavior - * @run testng TestDefaultBehavior + * @run junit ${test.main.class} */ import java.io.IOException; @@ -36,11 +36,13 @@ import java.net.SocketAddress; import java.net.SocketOption; import java.util.Set; -import org.testng.annotations.Test; import static java.lang.Boolean.*; import static java.net.StandardSocketOptions.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.expectThrows; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class TestDefaultBehavior { @@ -51,21 +53,21 @@ public class TestDefaultBehavior { public void datagramSocketImpl() { CustomDatagramSocketImpl dsi = new CustomDatagramSocketImpl(); - assertEquals(dsi.supportedOptions().size(), 0); - - expectThrows(NPE, () -> dsi.setOption(null, null)); - expectThrows(NPE, () -> dsi.setOption(null, 1)); - expectThrows(UOE, () -> dsi.setOption(SO_RCVBUF, 100)); - expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE)); - expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, FALSE)); - expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, TRUE)); - expectThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, FALSE)); - expectThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE)); - - expectThrows(NPE, () -> dsi.getOption(null)); - expectThrows(UOE, () -> dsi.getOption(SO_RCVBUF)); - expectThrows(UOE, () -> dsi.getOption(SO_KEEPALIVE)); - expectThrows(UOE, () -> dsi.getOption(FAKE_SOCK_OPT)); + assertEquals(0, dsi.supportedOptions().size()); + + assertThrows(NPE, () -> dsi.setOption(null, null)); + assertThrows(NPE, () -> dsi.setOption(null, 1)); + assertThrows(UOE, () -> dsi.setOption(SO_RCVBUF, 100)); + assertThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE)); + assertThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, FALSE)); + assertThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, TRUE)); + assertThrows(UOE, () -> dsi.setOption(FAKE_SOCK_OPT, FALSE)); + assertThrows(UOE, () -> dsi.setOption(SO_KEEPALIVE, TRUE)); + + assertThrows(NPE, () -> dsi.getOption(null)); + assertThrows(UOE, () -> dsi.getOption(SO_RCVBUF)); + assertThrows(UOE, () -> dsi.getOption(SO_KEEPALIVE)); + assertThrows(UOE, () -> dsi.getOption(FAKE_SOCK_OPT)); } static final SocketOption FAKE_SOCK_OPT = new SocketOption<>() { diff --git a/test/jdk/java/net/HttpURLConnection/ProxyBadStatusLine.java b/test/jdk/java/net/HttpURLConnection/ProxyBadStatusLine.java new file mode 100644 index 00000000000..be881030715 --- /dev/null +++ b/test/jdk/java/net/HttpURLConnection/ProxyBadStatusLine.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.util.List; + +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static java.net.Proxy.Type.HTTP; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/* @test + * @bug 8373778 + * @summary Verify that a IOException gets thrown from HttpURLConnection, if the proxy returns + * an invalid status line in response to a CONNECT request + * @library /test/lib + * @build jdk.test.lib.net.URIBuilder + * @run junit ${test.main.class} + */ +class ProxyBadStatusLine { + + static List badStatusLines() { + return List.of( + Arguments.of("", "Unexpected end of file from server"), + Arguments.of(" ", "Unexpected end of file from server"), + Arguments.of("\t", "Unexpected end of file from server"), + Arguments.of("\r\n", "Unexpected end of file from server"), + + Arguments.of("HTTP/1.", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.0", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1\r\n", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1\n", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1 301 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1 404 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1 503 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1\n200 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1\r200 ", "Unable to tunnel through proxy"), + Arguments.of("HTTP/1.1\f200 ", "Unable to tunnel through proxy") + ); + } + + /* + * Uses HttpURLConnection to initiate a HTTP request that results in a CONNECT + * request to a proxy server. The proxy server then responds with a bad status line. + * The test expects that an IOException gets thrown back to the application (instead + * of some unspecified exception). + */ + @ParameterizedTest + @MethodSource(value = "badStatusLines") + void testProxyConnectResponse(final String badStatusLine, final String expectedExceptionMsg) + throws Exception { + final InetSocketAddress irrelevantTargetServerAddr = + new InetSocketAddress(InetAddress.getLoopbackAddress(), 12345); + final URL url = URIBuilder.newBuilder() + .scheme("https") + .host(irrelevantTargetServerAddr.getAddress()) + .port(irrelevantTargetServerAddr.getPort()) + .path("/doesnotmatter") + .build().toURL(); + + Thread proxyServerThread = null; + try (final BadProxyServer proxy = new BadProxyServer(badStatusLine)) { + + proxyServerThread = Thread.ofPlatform().name("proxy-server").start(proxy); + final HttpURLConnection urlc = (HttpURLConnection) + url.openConnection(new Proxy(HTTP, proxy.getAddress())); + + final IOException ioe = assertThrows(IOException.class, () -> urlc.getInputStream()); + final String exMsg = ioe.getMessage(); + if (exMsg == null || !exMsg.contains(expectedExceptionMsg)) { + // unexpected message in the exception, propagate the exception + throw ioe; + } + System.err.println("got excepted exception: " + ioe); + } finally { + if (proxyServerThread != null) { + System.err.println("waiting for proxy server thread to complete"); + proxyServerThread.join(); + } + } + } + + private static final class BadProxyServer implements Runnable, AutoCloseable { + private static final int CR = '\r'; + private static final int LF = '\n'; + + private final ServerSocket serverSocket; + private final String connectRespStatusLine; + private volatile boolean closed; + + /** + * + * @param connectRespStatusLine the status line that this server writes + * out in response to a CONNECT request + * @throws IOException + */ + BadProxyServer(final String connectRespStatusLine) throws IOException { + this.connectRespStatusLine = connectRespStatusLine; + final int port = 0; + final int backlog = 0; + this.serverSocket = new ServerSocket(port, backlog, InetAddress.getLoopbackAddress()); + } + + InetSocketAddress getAddress() { + return (InetSocketAddress) this.serverSocket.getLocalSocketAddress(); + } + + @Override + public void close() { + if (this.closed) { + return; + } + synchronized (this) { + if (this.closed) { + return; + } + this.closed = true; + } + try { + this.serverSocket.close(); + } catch (IOException e) { + System.err.println("failed to close proxy server: " + e); + e.printStackTrace(); + } + } + + @Override + public void run() { + try { + doRun(); + } catch (Throwable t) { + if (!closed) { + System.err.println("Proxy server ran into exception: " + t); + t.printStackTrace(); + } + } + System.err.println("Proxy server " + this.serverSocket + " exiting"); + } + + private void doRun() throws IOException { + while (!closed) { + System.err.println("waiting for incoming connection at " + this.serverSocket); + try (final Socket accepted = this.serverSocket.accept()) { + System.err.println("accepted incoming connection from " + accepted); + handleIncomingConnection(accepted); + } + } + } + + private static int findCRLF(final byte[] b) { + for (int i = 0; i < b.length - 1; i++) { + if (b[i] == CR && b[i + 1] == LF) { + return i; + } + } + return -1; + } + + // writes out a status line in response to a CONNECT request + private void handleIncomingConnection(final Socket acceptedSocket) throws IOException { + final byte[] req = readRequest(acceptedSocket.getInputStream()); + final int crlfIndex = findCRLF(req); + if (crlfIndex < 0) { + System.err.println("unexpected request content from " + acceptedSocket); + // nothing to process, ignore this connection + return; + } + final String requestLine = new String(req, 0, crlfIndex, ISO_8859_1); + System.err.println("received request line: \"" + requestLine + "\""); + final String[] parts = requestLine.split(" "); + if (parts[0].equals("CONNECT")) { + // reply back with the status line + try (final OutputStream os = acceptedSocket.getOutputStream()) { + System.err.println("responding to CONNECT request from " + acceptedSocket + + ", response status line: \"" + connectRespStatusLine + "\""); + final byte[] statusLine = connectRespStatusLine.getBytes(ISO_8859_1); + os.write(statusLine); + } + } else { + System.err.println("unexpected request from " + acceptedSocket + ": \"" + requestLine + "\""); + return; + } + } + + private static byte[] readRequest(InputStream is) throws IOException { + // we don't expect the HTTP request body in this test to be larger than this size + final byte[] buff = new byte[4096]; + int crlfcount = 0; + int numRead = 0; + int c; + while ((c = is.read()) != -1 && numRead < buff.length) { + buff[numRead++] = (byte) c; + // + // HTTP-message = start-line CRLF + // *( field-line CRLF ) + // CRLF + // [ message-body ] + // + // start-line = request-line / status-line + // + // we are not interested in the message body, so this loop is + // looking for the CRLFCRLF sequence to stop parsing the request + // content + if (c == CR || c == LF) { + switch (crlfcount) { + case 0, 2 -> { + if (c == CR) { + crlfcount++; + } + } + case 1, 3 -> { + if (c == LF) { + crlfcount++; + } + } + } + } else { + crlfcount = 0; + } + if (crlfcount == 4) { + break; + } + } + if (crlfcount != 4) { + throw new IOException("Could not locate a CRLFCRLF sequence in the request"); + } + final byte[] request = new byte[numRead]; + System.arraycopy(buff, 0, request, 0, numRead); + return request; + } + } +} diff --git a/test/jdk/java/net/InetSocketAddress/ToString.java b/test/jdk/java/net/InetSocketAddress/ToString.java index 33fd1dd6a73..12eb599b872 100644 --- a/test/jdk/java/net/InetSocketAddress/ToString.java +++ b/test/jdk/java/net/InetSocketAddress/ToString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, 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 @@ -32,12 +32,15 @@ */ import java.net.*; +import java.util.Optional; -import jdk.test.lib.net.IPSupport; +import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; + public class ToString { private static final String loopbackAddr; @@ -74,7 +77,10 @@ public class ToString { @BeforeTest public void setup() { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); } @Test diff --git a/test/jdk/java/net/MulticastSocket/Constructor.java b/test/jdk/java/net/MulticastSocket/Constructor.java index 6a765cc2f04..296fe1ab105 100644 --- a/test/jdk/java/net/MulticastSocket/Constructor.java +++ b/test/jdk/java/net/MulticastSocket/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,15 +24,15 @@ /* @test * @bug 8243999 * @summary Checks to ensure that Multicast constructors behave as expected - * @run testng Constructor + * @run junit ${test.main.class} */ -import org.testng.annotations.Test; import java.io.IOException; import java.net.MulticastSocket; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; public class Constructor { @Test diff --git a/test/jdk/java/net/MulticastSocket/IPMulticastIF.java b/test/jdk/java/net/MulticastSocket/IPMulticastIF.java index 3909f6d6276..3d526c9b3d3 100644 --- a/test/jdk/java/net/MulticastSocket/IPMulticastIF.java +++ b/test/jdk/java/net/MulticastSocket/IPMulticastIF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,38 +27,41 @@ import java.net.NetworkInterface; import java.util.ArrayList; import java.util.List; + import jdk.test.lib.NetworkConfiguration; -import jdk.test.lib.net.IPSupport; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static java.lang.String.format; import static java.lang.System.out; import static java.net.StandardSocketOptions.IP_MULTICAST_IF; import static java.util.stream.Collectors.toList; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 8236441 * @summary Bound MulticastSocket fails when setting outbound interface on Windows * @library /test/lib - * @run testng IPMulticastIF - * @run testng/othervm -Djava.net.preferIPv4Stack=true IPMulticastIF - * @run testng/othervm -Djava.net.preferIPv6Addresses=true IPMulticastIF - * @run testng/othervm -Djava.net.preferIPv6Addresses=true -Djava.net.preferIPv4Stack=true IPMulticastIF + * @run junit ${test.main.class} + * @run junit/othervm -Djava.net.preferIPv4Stack=true ${test.main.class} + * @run junit/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + * @run junit/othervm -Djava.net.preferIPv6Addresses=true -Djava.net.preferIPv4Stack=true ${test.main.class} */ public class IPMulticastIF { - @BeforeTest - public void sanity() { - IPSupport.throwSkippedExceptionIfNonOperational(); + @BeforeAll + public static void sanity() { + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); NetworkConfiguration.printSystemConfiguration(out); } - @DataProvider(name = "scenarios") - public Object[][] positive() throws Exception { + public static Object[][] positive() throws Exception { List addrs = List.of(InetAddress.getLocalHost(), InetAddress.getLoopbackAddress()); List list = new ArrayList<>(); @@ -75,8 +78,7 @@ public Object[][] positive() throws Exception { return list.stream().toArray(Object[][]::new); } - @DataProvider(name = "interfaces") - public Object[][] interfaces() throws Exception { + public static Object[][] interfaces() throws Exception { List list = new ArrayList<>(); NetworkConfiguration nc = NetworkConfiguration.probe(); nc.multicastInterfaces(true) @@ -86,7 +88,8 @@ public Object[][] interfaces() throws Exception { return list.stream().toArray(Object[][]::new); } - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("positive") public void testSetGetInterfaceBound(InetSocketAddress bindAddr, NetworkInterface nif) throws Exception { @@ -94,11 +97,12 @@ public void testSetGetInterfaceBound(InetSocketAddress bindAddr, NetworkInterfac try (MulticastSocket ms = new MulticastSocket(bindAddr)) { ms.setNetworkInterface(nif); NetworkInterface msNetIf = ms.getNetworkInterface(); - assertEquals(msNetIf, nif); + assertEquals(nif, msNetIf); } } - @Test(dataProvider = "interfaces") + @ParameterizedTest + @MethodSource("interfaces") public void testSetGetInterfaceUnbound(NetworkInterface nif) throws Exception { @@ -106,11 +110,12 @@ public void testSetGetInterfaceUnbound(NetworkInterface nif) try (MulticastSocket ms = new MulticastSocket()) { ms.setNetworkInterface(nif); NetworkInterface msNetIf = ms.getNetworkInterface(); - assertEquals(msNetIf, nif); + assertEquals(nif, msNetIf); } } - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("positive") public void testSetGetOptionBound(InetSocketAddress bindAddr, NetworkInterface nif) throws Exception { @@ -118,11 +123,12 @@ public void testSetGetOptionBound(InetSocketAddress bindAddr, NetworkInterface n try (MulticastSocket ms = new MulticastSocket(bindAddr)) { ms.setOption(IP_MULTICAST_IF, nif); NetworkInterface msNetIf = ms.getOption(IP_MULTICAST_IF); - assertEquals(msNetIf, nif); + assertEquals(nif, msNetIf); } } - @Test(dataProvider = "interfaces") + @ParameterizedTest + @MethodSource("interfaces") public void testSetGetOptionUnbound(NetworkInterface nif) throws Exception { @@ -130,21 +136,21 @@ public void testSetGetOptionUnbound(NetworkInterface nif) try (MulticastSocket ms = new MulticastSocket()) { ms.setOption(IP_MULTICAST_IF, nif); NetworkInterface msNetIf = ms.getOption(IP_MULTICAST_IF); - assertEquals(msNetIf, nif); + assertEquals(nif, msNetIf); } } // -- get without set - @DataProvider(name = "bindAddresses") - public Object[][] bindAddresses() throws Exception { + public static Object[][] bindAddresses() throws Exception { return new Object[][] { { new InetSocketAddress(InetAddress.getLocalHost(), 0) }, { new InetSocketAddress(InetAddress.getLoopbackAddress(), 0) }, }; } - @Test(dataProvider = "bindAddresses") + @ParameterizedTest + @MethodSource("bindAddresses") public void testGetInterfaceBound(InetSocketAddress bindAddr) throws Exception { @@ -162,13 +168,14 @@ public void testGetInterfaceUnbound() throws Exception { } } - @Test(dataProvider = "bindAddresses") + @ParameterizedTest + @MethodSource("bindAddresses") public void testGetOptionBound(InetSocketAddress bindAddr) throws Exception { out.println(format("\n\n--- testGetOptionBound bindAddr=[%s]", bindAddr)); try (MulticastSocket ms = new MulticastSocket(bindAddr)) { - assertEquals(ms.getOption(IP_MULTICAST_IF), null); + assertEquals(null, ms.getOption(IP_MULTICAST_IF)); } } @@ -176,7 +183,7 @@ public void testGetOptionBound(InetSocketAddress bindAddr) public void testGetOptionUnbound() throws Exception { out.println("\n\n--- testGetOptionUnbound "); try (MulticastSocket ms = new MulticastSocket()) { - assertEquals(ms.getOption(IP_MULTICAST_IF), null); + assertEquals(null, ms.getOption(IP_MULTICAST_IF)); } } @@ -184,7 +191,7 @@ public void testGetOptionUnbound() throws Exception { // that represent any local address. static void assertPlaceHolder(NetworkInterface nif) { List addrs = nif.inetAddresses().collect(toList()); - assertEquals(addrs.size(), 1); + assertEquals(1, addrs.size()); assertTrue(addrs.get(0).isAnyLocalAddress()); } } diff --git a/test/jdk/java/net/MulticastSocket/SendPortZero.java b/test/jdk/java/net/MulticastSocket/SendPortZero.java index 0d8acd86c8c..ab06dfdd45d 100644 --- a/test/jdk/java/net/MulticastSocket/SendPortZero.java +++ b/test/jdk/java/net/MulticastSocket/SendPortZero.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 @@ -21,39 +21,39 @@ * questions. */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.DatagramPacket; import java.net.MulticastSocket; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.MulticastSocket; import java.net.SocketException; -import java.nio.channels.DatagramChannel; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.api.AfterAll; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8243408 * @summary Check that MulticastSocket throws expected * Exception when sending a DatagramPacket with port 0 - * @run testng/othervm SendPortZero + * @run junit/othervm ${test.main.class} */ public class SendPortZero { - private InetAddress loopbackAddr, wildcardAddr; - private MulticastSocket multicastSocket; - private DatagramPacket loopbackZeroPkt, wildcardZeroPkt, wildcardValidPkt; + private static InetAddress loopbackAddr, wildcardAddr; + private static MulticastSocket multicastSocket; + private static DatagramPacket loopbackZeroPkt, wildcardZeroPkt, wildcardValidPkt; private static final Class SE = SocketException.class; - @BeforeTest - public void setUp() throws IOException { + @BeforeAll + public static void setUp() throws IOException { multicastSocket = new MulticastSocket(); byte[] buf = "test".getBytes(); @@ -80,23 +80,26 @@ public void setUp() throws IOException { wildcardValidPkt.setPort(multicastSocket.getLocalPort()); } - @DataProvider(name = "data") - public Object[][] variants() { + public static Object[][] testCases() throws IOException { return new Object[][]{ - { multicastSocket, loopbackZeroPkt }, - { multicastSocket, wildcardZeroPkt }, + { new MulticastSocket(), loopbackZeroPkt }, + { new MulticastSocket(), wildcardZeroPkt }, // Not currently tested. See JDK-8236807 - //{ multicastSocket, wildcardValidPkt } + //{ new MulticastSocket(), wildcardValidPkt } }; } - @Test(dataProvider = "data") + @ParameterizedTest + @MethodSource("testCases") public void testSend(MulticastSocket ms, DatagramPacket pkt) { - assertThrows(SE, () -> ms.send(pkt)); + try (ms) { + assertFalse(ms.isClosed()); + assertThrows(SE, () -> ms.send(pkt)); + } } - @AfterTest - public void tearDown() { + @AfterAll + public static void tearDown() { multicastSocket.close(); } } diff --git a/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java b/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java index ed831cfbdf7..8185c71345d 100644 --- a/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java +++ b/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,9 @@ * return the correct result for StandardSocketOptions.IP_MULTICAST_LOOP. * The test sets a DatagramSocketImplFactory and needs to run in /othervm * mode. - * @run testng/othervm SetLoopbackOption - * @run testng/othervm -Djava.net.preferIPv4Stack=true SetLoopbackOption - * @run testng/othervm -Djava.net.preferIPv6Addresses=true SetLoopbackOption + * @run junit/othervm ${test.main.class} + * @run junit/othervm -Djava.net.preferIPv4Stack=true ${test.main.class} + * @run junit/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} */ import java.io.FileDescriptor; @@ -52,11 +52,12 @@ import java.util.Map; import java.util.Set; -import org.testng.annotations.Test; -import static org.testng.Assert.*; - import static java.lang.System.out; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class SetLoopbackOption { final InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); diff --git a/test/jdk/java/net/NetworkInterface/NetworkInterfaceStreamTest.java b/test/jdk/java/net/NetworkInterface/NetworkInterfaceStreamTest.java index 63093e4b9fa..000b802d9a9 100644 --- a/test/jdk/java/net/NetworkInterface/NetworkInterfaceStreamTest.java +++ b/test/jdk/java/net/NetworkInterface/NetworkInterfaceStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, 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 @@ -30,6 +30,7 @@ * @run testng/othervm -Djava.net.preferIPv4Stack=true NetworkInterfaceStreamTest */ +import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -39,12 +40,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Optional; import java.util.function.Supplier; import java.util.stream.OpTestCase; import java.util.stream.Stream; import java.util.stream.TestData; -import jdk.test.lib.net.IPSupport; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; public class NetworkInterfaceStreamTest extends OpTestCase { @@ -52,7 +54,10 @@ public class NetworkInterfaceStreamTest extends OpTestCase { @BeforeTest void setup() { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); } @Test diff --git a/test/jdk/java/net/NetworkInterface/NullMacAddress.java b/test/jdk/java/net/NetworkInterface/NullMacAddress.java index b31e93d39f2..8161670860e 100644 --- a/test/jdk/java/net/NetworkInterface/NullMacAddress.java +++ b/test/jdk/java/net/NetworkInterface/NullMacAddress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,11 @@ * @run testng/othervm -Djava.net.preferIPv4Stack=true NullMacAddress */ +import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; + +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.*; import java.io.UncheckedIOException; @@ -40,14 +43,17 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.util.Locale; +import java.util.Optional; -import jdk.test.lib.net.IPSupport; public class NullMacAddress { @BeforeTest void setup() { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); } @Test diff --git a/test/jdk/java/net/Socket/ConnectionReset.java b/test/jdk/java/net/Socket/ConnectionReset.java index 41be61ac7e1..630bd61a606 100644 --- a/test/jdk/java/net/Socket/ConnectionReset.java +++ b/test/jdk/java/net/Socket/ConnectionReset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /** * @test - * @run testng ConnectionReset + * @run junit ${test.main.class} * @summary Test behavior of read and available when a connection is reset */ @@ -34,10 +34,13 @@ import java.net.ServerSocket; import java.net.Socket; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -@Test public class ConnectionReset { static final int REPEAT_COUNT = 5; @@ -45,25 +48,23 @@ public class ConnectionReset { /** * Tests available before read when there are no bytes to read */ + @Test public void testAvailableBeforeRead1() throws IOException { - System.out.println("testAvailableBeforeRead1"); withResetConnection(null, s -> { InputStream in = s.getInputStream(); for (int i=0; i %d%n", bytesAvailable); - assertTrue(bytesAvailable == 0); - try { + System.err.format("available => %d%n", bytesAvailable); + assertEquals(0, bytesAvailable); + IOException ioe = assertThrows(IOException.class, () -> { int bytesRead = in.read(); if (bytesRead == -1) { - System.out.println("read => EOF"); + System.err.println("read => EOF"); } else { - System.out.println("read => 1 byte"); + System.err.println("read => 1 byte"); } - assertTrue(false); - } catch (IOException ioe) { - System.out.format("read => %s (expected)%n", ioe); - } + }); + System.err.format("read => %s (expected)%n", ioe); } }); } @@ -71,28 +72,25 @@ public void testAvailableBeforeRead1() throws IOException { /** * Tests available before read when there are bytes to read */ + @Test public void testAvailableBeforeRead2() throws IOException { - System.out.println("testAvailableBeforeRead2"); byte[] data = { 1, 2, 3 }; withResetConnection(data, s -> { InputStream in = s.getInputStream(); int remaining = data.length; for (int i=0; i %d%n", bytesAvailable); + System.err.format("available => %d%n", bytesAvailable); assertTrue(bytesAvailable <= remaining); try { int bytesRead = in.read(); - if (bytesRead == -1) { - System.out.println("read => EOF"); - assertTrue(false); - } else { - System.out.println("read => 1 byte"); - assertTrue(remaining > 0); - remaining--; - } + assertNotEquals(-1, bytesRead, "EOF not expected"); + + System.err.println("read => 1 byte"); + assertTrue(remaining > 0); + remaining--; } catch (IOException ioe) { - System.out.format("read => %s%n", ioe); + System.err.format("read => %s%n", ioe); remaining = 0; } } @@ -102,25 +100,24 @@ public void testAvailableBeforeRead2() throws IOException { /** * Tests read before available when there are no bytes to read */ + @Test public void testReadBeforeAvailable1() throws IOException { - System.out.println("testReadBeforeAvailable1"); withResetConnection(null, s -> { InputStream in = s.getInputStream(); for (int i=0; i { int bytesRead = in.read(); if (bytesRead == -1) { - System.out.println("read => EOF"); + System.err.println("read => EOF"); } else { - System.out.println("read => 1 byte"); + System.err.println("read => 1 byte"); } - assertTrue(false); - } catch (IOException ioe) { - System.out.format("read => %s (expected)%n", ioe); - } + }); + System.err.format("read => %s (expected)%n", ioe); + int bytesAvailable = in.available(); - System.out.format("available => %d%n", bytesAvailable); - assertTrue(bytesAvailable == 0); + System.err.format("available => %d%n", bytesAvailable); + assertEquals(0, bytesAvailable); } }); } @@ -128,8 +125,8 @@ public void testReadBeforeAvailable1() throws IOException { /** * Tests read before available when there are bytes to read */ + @Test public void testReadBeforeAvailable2() throws IOException { - System.out.println("testReadBeforeAvailable2"); byte[] data = { 1, 2, 3 }; withResetConnection(data, s -> { InputStream in = s.getInputStream(); @@ -137,20 +134,17 @@ public void testReadBeforeAvailable2() throws IOException { for (int i=0; i EOF"); - assertTrue(false); - } else { - System.out.println("read => 1 byte"); - assertTrue(remaining > 0); - remaining--; - } + assertNotEquals(-1, bytesRead, "EOF not expected"); + + System.err.println("read => 1 byte"); + assertTrue(remaining > 0); + remaining--; } catch (IOException ioe) { - System.out.format("read => %s%n", ioe); + System.err.format("read => %s%n", ioe); remaining = 0; } int bytesAvailable = in.available(); - System.out.format("available => %d%n", bytesAvailable); + System.err.format("available => %d%n", bytesAvailable); assertTrue(bytesAvailable <= remaining); } }); @@ -159,31 +153,22 @@ public void testReadBeforeAvailable2() throws IOException { /** * Tests available and read on a socket closed after connection reset */ + @Test public void testAfterClose() throws IOException { - System.out.println("testAfterClose"); withResetConnection(null, s -> { InputStream in = s.getInputStream(); - try { - in.read(); - assertTrue(false); - } catch (IOException ioe) { - // expected - } + assertThrows(IOException.class, () -> in.read()); s.close(); - try { + IOException ioe = assertThrows(IOException.class, () -> { int bytesAvailable = in.available(); - System.out.format("available => %d%n", bytesAvailable); - assertTrue(false); - } catch (IOException ioe) { - System.out.format("available => %s (expected)%n", ioe); - } - try { + System.err.format("available => %d%n", bytesAvailable); + }); + System.err.format("available => %s (expected)%n", ioe); + ioe = assertThrows(IOException.class, () -> { int n = in.read(); - System.out.format("read => %d%n", n); - assertTrue(false); - } catch (IOException ioe) { - System.out.format("read => %s (expected)%n", ioe); - } + System.err.format("read => %d%n", n); + }); + System.err.format("read => %s (expected)%n", ioe); }); } diff --git a/test/jdk/java/net/Socket/UdpSocket.java b/test/jdk/java/net/Socket/UdpSocket.java index 7e93343cefb..47bf09d1334 100644 --- a/test/jdk/java/net/Socket/UdpSocket.java +++ b/test/jdk/java/net/Socket/UdpSocket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,13 @@ import java.net.InetAddress; import java.net.Socket; -import org.testng.annotations.Test; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.fail; /* * @test * @summary Basic test for the UDP sockets through the java.net.Socket constructors - * @run testng UdpSocket + * @run junit ${test.main.class} */ public class UdpSocket { diff --git a/test/jdk/java/net/SocketImpl/BadUsages.java b/test/jdk/java/net/SocketImpl/BadUsages.java index b5d85708dde..ca0ab6fe71e 100644 --- a/test/jdk/java/net/SocketImpl/BadUsages.java +++ b/test/jdk/java/net/SocketImpl/BadUsages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @bug 8221481 * @summary Test the platform SocketImpl when used in unintended ways * @compile/module=java.base java/net/PlatformSocketImpl.java - * @run testng/othervm BadUsages + * @run junit/othervm ${test.main.class} */ import java.io.IOException; @@ -41,8 +41,8 @@ import java.net.PlatformSocketImpl; // test helper -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * SocketImpl does not specify how the SocketImpl behaves when used in ways @@ -54,74 +54,80 @@ * throws reasonable exceptions, for these scenarios. */ -@Test public class BadUsages { /** * Test create when already created. */ + @Test public void testCreate1() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.create(true)); + assertThrows(IOException.class, () -> impl.create(true)); } } /** * Test create when closed. */ + @Test public void testCreate2() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.create(true)); + assertThrows(IOException.class, () -> impl.create(true)); } /** * Test create when not a stream socket. */ + @Test public void testCreate3() throws IOException { try (var impl = new PlatformSocketImpl(false)) { - expectThrows(IOException.class, () -> impl.create(false)); + assertThrows(IOException.class, () -> impl.create(false)); } } /** * Test connect when not created. */ + @Test public void testConnect1() throws IOException { try (var ss = new ServerSocket(0)) { var impl = new PlatformSocketImpl(false); var address = ss.getInetAddress(); int port = ss.getLocalPort(); - expectThrows(IOException.class, () -> impl.connect(address, port)); + assertThrows(IOException.class, () -> impl.connect(address, port)); } } /** * Test connect with unsupported address type. */ + @Test public void testConnect2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); var remote = new SocketAddress() { }; - expectThrows(IOException.class, () -> impl.connect(remote, 0)); + assertThrows(IOException.class, () -> impl.connect(remote, 0)); } } /** * Test connect with an unresolved address. */ + @Test public void testConnect3() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); var remote = new InetSocketAddress("blah-blah.blah-blah", 80); - expectThrows(IOException.class, () -> impl.connect(remote, 0)); + assertThrows(IOException.class, () -> impl.connect(remote, 0)); } } /** * Test connect when already connected. */ + @Test public void testConnect4() throws IOException { try (var ss = new ServerSocket(); var impl = new PlatformSocketImpl(false)) { @@ -130,47 +136,51 @@ public void testConnect4() throws IOException { impl.create(true); int port = ss.getLocalPort(); impl.connect(loopback, port); - expectThrows(IOException.class, () -> impl.connect(loopback, port)); + assertThrows(IOException.class, () -> impl.connect(loopback, port)); } } /** * Test connect when closed. */ + @Test public void testConnect5() throws IOException { try (var ss = new ServerSocket(0)) { var impl = new PlatformSocketImpl(false); impl.close(); String host = ss.getInetAddress().getHostAddress(); int port = ss.getLocalPort(); - expectThrows(IOException.class, () -> impl.connect(host, port)); + assertThrows(IOException.class, () -> impl.connect(host, port)); } } /** * Test bind when not created. */ + @Test public void testBind1() throws IOException { var impl = new PlatformSocketImpl(false); var loopback = InetAddress.getLoopbackAddress(); - expectThrows(IOException.class, () -> impl.bind(loopback, 0)); + assertThrows(IOException.class, () -> impl.bind(loopback, 0)); } /** * Test bind when already bound. */ + @Test public void testBind2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); var loopback = InetAddress.getLoopbackAddress(); impl.bind(loopback, 0); - expectThrows(IOException.class, () -> impl.bind(loopback, 0)); + assertThrows(IOException.class, () -> impl.bind(loopback, 0)); } } /** * Test bind when connected. */ + @Test public void testBind3() throws IOException { try (var ss = new ServerSocket(); var impl = new PlatformSocketImpl(false)) { @@ -178,94 +188,103 @@ public void testBind3() throws IOException { ss.bind(new InetSocketAddress(loopback, 0)); impl.create(true); impl.connect(ss.getLocalSocketAddress(), 0); - expectThrows(IOException.class, () -> impl.bind(loopback, 0)); + assertThrows(IOException.class, () -> impl.bind(loopback, 0)); } } /** * Test bind when closed. */ + @Test public void testBind4() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); var loopback = InetAddress.getLoopbackAddress(); - expectThrows(IOException.class, () -> impl.bind(loopback, 0)); + assertThrows(IOException.class, () -> impl.bind(loopback, 0)); } /** * Test listen when not created. */ + @Test public void testListen1() { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.listen(16)); + assertThrows(IOException.class, () -> impl.listen(16)); } /** * Test listen when not bound. */ + @Test public void testListen2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.listen(16)); + assertThrows(IOException.class, () -> impl.listen(16)); } } /** * Test listen when closed. */ + @Test public void testListen3() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.listen(16)); + assertThrows(IOException.class, () -> impl.listen(16)); } /** * Test accept when not created. */ + @Test public void testAccept1() throws IOException { var impl = new PlatformSocketImpl(true); var si = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.accept(si)); + assertThrows(IOException.class, () -> impl.accept(si)); } /** * Test accept when not bound. */ + @Test public void testAccept2() throws IOException { try (var impl = new PlatformSocketImpl(true)) { impl.create(true); var si = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.accept(si)); + assertThrows(IOException.class, () -> impl.accept(si)); } } /** * Test accept when closed. */ + @Test public void testAccept4() throws IOException { var impl = new PlatformSocketImpl(true); impl.close(); var si = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.accept(si)); + assertThrows(IOException.class, () -> impl.accept(si)); } /** * Test accept with SocketImpl that is already created. */ + @Test public void testAccept5() throws IOException { try (var impl = new PlatformSocketImpl(true); var si = new PlatformSocketImpl(false)) { impl.create(true); impl.bind(InetAddress.getLoopbackAddress(), 0); si.create(true); - expectThrows(IOException.class, () -> impl.accept(si)); + assertThrows(IOException.class, () -> impl.accept(si)); } } /** * Test accept with SocketImpl that is closed. */ + @Test public void testAccept6() throws IOException { try (var impl = new PlatformSocketImpl(true); var si = new PlatformSocketImpl(false)) { @@ -273,65 +292,71 @@ public void testAccept6() throws IOException { impl.bind(InetAddress.getLoopbackAddress(), 0); si.create(true); si.close(); - expectThrows(IOException.class, () -> impl.accept(si)); + assertThrows(IOException.class, () -> impl.accept(si)); } } /** * Test available when not created. */ + @Test public void testAvailable1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.available()); + assertThrows(IOException.class, () -> impl.available()); } /** * Test available when created but not connected. */ + @Test public void testAvailable2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.available()); + assertThrows(IOException.class, () -> impl.available()); } } /** * Test available when closed. */ + @Test public void testAvailable3() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.available()); + assertThrows(IOException.class, () -> impl.available()); } /** * Test setOption when not created. */ + @Test public void testSetOption1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, + assertThrows(IOException.class, () -> impl.setOption(StandardSocketOptions.SO_REUSEADDR, true)); // legacy - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_REUSEADDR, true)); } /** * Test setOption when closed. */ + @Test public void testSetOption2() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, + assertThrows(IOException.class, () -> impl.setOption(StandardSocketOptions.SO_REUSEADDR, true)); // legacy - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_REUSEADDR, true)); } /** * Test setOption with unsupported option. */ + @Test public void testSetOption3() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); @@ -339,25 +364,26 @@ public void testSetOption3() throws IOException { @Override public String name() { return "birthday"; } @Override public Class type() { return String.class; } }; - expectThrows(UnsupportedOperationException.class, () -> impl.setOption(opt, "")); + assertThrows(UnsupportedOperationException.class, () -> impl.setOption(opt, "")); // legacy - expectThrows(SocketException.class, () -> impl.setOption(-1, "")); + assertThrows(SocketException.class, () -> impl.setOption(-1, "")); } } /** * Test setOption(int, Object) with invalid values. */ + @Test public void testSetOption4() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_REUSEADDR, -1)); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_TIMEOUT, -1)); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_SNDBUF, -1)); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.setOption(SocketOptions.SO_RCVBUF, -1)); } } @@ -365,29 +391,32 @@ public void testSetOption4() throws IOException { /** * Test getOption when not created. */ + @Test public void testGetOption1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, + assertThrows(IOException.class, () -> impl.getOption(StandardSocketOptions.SO_REUSEADDR)); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.getOption(-1)); } /** * Test getOption when closed. */ + @Test public void testGetOption2() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, + assertThrows(IOException.class, () -> impl.getOption(StandardSocketOptions.SO_REUSEADDR)); - expectThrows(SocketException.class, + assertThrows(SocketException.class, () -> impl.getOption(SocketOptions.SO_REUSEADDR)); } /** * Test getOption with unsupported option. */ + @Test public void testGetOption3() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); @@ -395,89 +424,98 @@ public void testGetOption3() throws IOException { @Override public String name() { return "birthday"; } @Override public Class type() { return String.class; } }; - expectThrows(UnsupportedOperationException.class, () -> impl.getOption(opt)); - expectThrows(SocketException.class, () -> impl.getOption(-1)); + assertThrows(UnsupportedOperationException.class, () -> impl.getOption(opt)); + assertThrows(SocketException.class, () -> impl.getOption(-1)); } } /** * Test shutdownInput when not created. */ + @Test public void testShutdownInput1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.shutdownInput()); + assertThrows(IOException.class, () -> impl.shutdownInput()); } /** * Test shutdownInput when not connected. */ + @Test public void testShutdownInput2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.shutdownInput()); + assertThrows(IOException.class, () -> impl.shutdownInput()); } } /** * Test shutdownInput when closed. */ + @Test public void testShutdownInput3() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.shutdownInput()); + assertThrows(IOException.class, () -> impl.shutdownInput()); } /** * Test shutdownOutput when not created. */ + @Test public void testShutdownOutput1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.shutdownOutput()); + assertThrows(IOException.class, () -> impl.shutdownOutput()); } /** * Test shutdownOutput when not connected. */ + @Test public void testShutdownOutput2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.shutdownOutput()); + assertThrows(IOException.class, () -> impl.shutdownOutput()); } } /** * Test shutdownOutput when closed. */ + @Test public void testShutdownOutput3() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.shutdownOutput()); + assertThrows(IOException.class, () -> impl.shutdownOutput()); } /** * Test sendUrgentData when not created. */ + @Test public void testSendUrgentData1() throws IOException { var impl = new PlatformSocketImpl(false); - expectThrows(IOException.class, () -> impl.sendUrgentData(0)); + assertThrows(IOException.class, () -> impl.sendUrgentData(0)); } /** * Test sendUrgentData when not connected. */ + @Test public void testSendUrgentData2() throws IOException { try (var impl = new PlatformSocketImpl(false)) { impl.create(true); - expectThrows(IOException.class, () -> impl.sendUrgentData(0)); + assertThrows(IOException.class, () -> impl.sendUrgentData(0)); } } /** * Test sendUrgentData when closed. */ + @Test public void testSendUrgentData3() throws IOException { var impl = new PlatformSocketImpl(false); impl.close(); - expectThrows(IOException.class, () -> impl.sendUrgentData(0)); + assertThrows(IOException.class, () -> impl.sendUrgentData(0)); } } diff --git a/test/jdk/java/net/SocketImpl/ImplSupportedOptions.java b/test/jdk/java/net/SocketImpl/ImplSupportedOptions.java index 51d523dce82..422f292bd97 100644 --- a/test/jdk/java/net/SocketImpl/ImplSupportedOptions.java +++ b/test/jdk/java/net/SocketImpl/ImplSupportedOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 8213418 * @summary Ensure correct impl supported socket options - * @run testng ImplSupportedOptions + * @run junit ${test.main.class} */ import java.io.IOException; @@ -40,9 +40,10 @@ import java.net.SocketOption; import java.net.StandardSocketOptions; import java.util.Set; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ImplSupportedOptions { @@ -52,29 +53,29 @@ public void socketSupportedOptions() throws Exception { Set standardOptions = s.supportedOptions(); assertTrue(standardOptions.contains(StandardSocketOptions.SO_LINGER), "Expected SO_LINGER, in:" + standardOptions); - assertEquals(standardOptions, s.supportedOptions()); - assertEquals(standardOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), standardOptions); + assertEquals(s.supportedOptions(), standardOptions); s = new DummySocket(); Set dummyOptions = s.supportedOptions(); - assertEquals(dummyOptions.size(), 1); + assertEquals(1, dummyOptions.size()); assertTrue(dummyOptions.contains(DummySocketImpl.SOCKET_OPT)); - assertEquals(dummyOptions, s.supportedOptions()); - assertEquals(dummyOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), dummyOptions); + assertEquals(s.supportedOptions(), dummyOptions); s = new Socket(); standardOptions = s.supportedOptions(); assertTrue(standardOptions.contains(StandardSocketOptions.SO_LINGER), "Expected SO_LINGER, in:" + standardOptions); - assertEquals(standardOptions, s.supportedOptions()); - assertEquals(standardOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), standardOptions); + assertEquals(s.supportedOptions(), standardOptions); s = new DummySocket(); dummyOptions = s.supportedOptions(); - assertEquals(dummyOptions.size(), 1); + assertEquals(1, dummyOptions.size()); assertTrue(dummyOptions.contains(DummySocketImpl.SOCKET_OPT)); - assertEquals(dummyOptions, s.supportedOptions()); - assertEquals(dummyOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), dummyOptions); + assertEquals(s.supportedOptions(), dummyOptions); } @Test @@ -83,29 +84,29 @@ public void serverSocketSupportedOptions() throws Exception { Set standardOptions = s.supportedOptions(); assertTrue(standardOptions.contains(StandardSocketOptions.SO_REUSEADDR), "Expected SO_REUSEADDR, in:" + standardOptions); - assertEquals(standardOptions, s.supportedOptions()); - assertEquals(standardOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), standardOptions); + assertEquals(s.supportedOptions(), standardOptions); s = new DummyServerSocket(); Set dummyOptions = s.supportedOptions(); - assertEquals(dummyOptions.size(), 1); + assertEquals(1, dummyOptions.size()); assertTrue(dummyOptions.contains(DummySocketImpl.SOCKET_OPT)); - assertEquals(dummyOptions, s.supportedOptions()); - assertEquals(dummyOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), dummyOptions); + assertEquals(s.supportedOptions(), dummyOptions); s = new ServerSocket(); standardOptions = s.supportedOptions(); assertTrue(standardOptions.contains(StandardSocketOptions.SO_REUSEADDR), "Expected SO_REUSEADDR, in:" + standardOptions); - assertEquals(standardOptions, s.supportedOptions()); - assertEquals(standardOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), standardOptions); + assertEquals(s.supportedOptions(), standardOptions); s = new DummyServerSocket(); dummyOptions = s.supportedOptions(); - assertEquals(dummyOptions.size(), 1); + assertEquals(1, dummyOptions.size()); assertTrue(dummyOptions.contains(DummySocketImpl.SOCKET_OPT)); - assertEquals(dummyOptions, s.supportedOptions()); - assertEquals(dummyOptions, s.supportedOptions()); + assertEquals(s.supportedOptions(), dummyOptions); + assertEquals(s.supportedOptions(), dummyOptions); } static class DummySocket extends Socket { diff --git a/test/jdk/java/net/SocketImpl/SocketImplCombinations.java b/test/jdk/java/net/SocketImpl/SocketImplCombinations.java index 8fe8600bbb3..3a7968db909 100644 --- a/test/jdk/java/net/SocketImpl/SocketImplCombinations.java +++ b/test/jdk/java/net/SocketImpl/SocketImplCombinations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 8220493 * @modules java.base/java.net:+open java.base/sun.nio.ch:+open - * @run testng/othervm SocketImplCombinations + * @run junit/othervm ${test.main.class} * @summary Test Socket and ServerSocket with combinations of SocketImpls */ @@ -46,15 +46,22 @@ import java.nio.channels.SocketChannel; import java.util.function.BiConsumer; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -@Test public class SocketImplCombinations { /** * Test creating an unconnected Socket, it should be created with a platform SocketImpl. */ + @Test public void testNewSocket1() throws IOException { try (Socket s = new Socket()) { SocketImpl si = getSocketImpl(s); @@ -67,6 +74,7 @@ public void testNewSocket1() throws IOException { /** * Test creating a connected Socket, it should be created with a platform SocketImpl. */ + @Test public void testNewSocket2() throws IOException { try (ServerSocket ss = boundServerSocket()) { try (Socket s = new Socket(ss.getInetAddress(), ss.getLocalPort())) { @@ -82,6 +90,7 @@ public void testNewSocket2() throws IOException { * Test creating a Socket for a DIRECT connection, it should be created with a * platform SocketImpl. */ + @Test public void testNewSocket3() throws IOException { try (Socket s = new Socket(Proxy.NO_PROXY)) { SocketImpl si = getSocketImpl(s); @@ -93,6 +102,7 @@ public void testNewSocket3() throws IOException { * Test creating a Socket for a SOCKS connection, it should be created with a * SOCKS SocketImpl. */ + @Test public void testNewSocket4() throws IOException { var address = new InetSocketAddress("127.0.0.1", 1080); var socksProxy = new Proxy(Proxy.Type.SOCKS, address); @@ -105,9 +115,10 @@ public void testNewSocket4() throws IOException { } /** - * Test creating a Socket for a HTTP proxy connection, it should be created with - * a HTTP proxy SocketImpl. + * Test creating a Socket for an HTTP proxy connection, it should be created with + * an HTTP proxy SocketImpl. */ + @Test public void testNewSocket5() throws IOException { var address = new InetSocketAddress("127.0.0.1", 8080); var httpProxy = new Proxy(Proxy.Type.HTTP, address); @@ -123,10 +134,11 @@ public void testNewSocket5() throws IOException { * Test creating a Socket no SocketImpl. A platform SocketImpl should be * created lazily. */ + @Test public void testNewSocket6() throws IOException { Socket s = new Socket((SocketImpl) null) { }; try (s) { - assertTrue(getSocketImpl(s) == null); + assertNull(getSocketImpl(s)); s.bind(loopbackSocketAddress()); // force SocketImpl to be created SocketImpl si = getSocketImpl(s); assertTrue(isSocksSocketImpl(si)); @@ -138,22 +150,24 @@ public void testNewSocket6() throws IOException { /** * Test creating a Socket with a custom SocketImpl. */ + @Test public void testNewSocket7() throws IOException { Socket s = new Socket(new CustomSocketImpl(false)) { }; try (s) { SocketImpl si = getSocketImpl(s); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } } /** * Test creating a Socket when there is a SocketImplFactory set. */ + @Test public void testNewSocket8() throws IOException { setSocketSocketImplFactory(() -> new CustomSocketImpl(false)); try (Socket s = new Socket()) { SocketImpl si = getSocketImpl(s); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } finally { setSocketSocketImplFactory(null); } @@ -163,11 +177,12 @@ public void testNewSocket8() throws IOException { * Test creating a Socket for a DIRECT connection when there is a * SocketImplFactory set. */ + @Test public void testNewSocket9() throws IOException { setSocketSocketImplFactory(() -> new CustomSocketImpl(false)); try (Socket s = new Socket(Proxy.NO_PROXY)) { SocketImpl si = getSocketImpl(s); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } finally { setSocketSocketImplFactory(null); } @@ -177,6 +192,7 @@ public void testNewSocket9() throws IOException { * Test creating a Socket for a SOCKS connection when there is a * SocketImplFactory set. */ + @Test public void testNewSocket10() throws IOException { var address = new InetSocketAddress("127.0.0.1", 1080); var socksProxy = new Proxy(Proxy.Type.SOCKS, address); @@ -192,9 +208,10 @@ public void testNewSocket10() throws IOException { } /** - * Test creating a Socket for a HTTP proxy connection when there is a + * Test creating a Socket for an HTTP proxy connection when there is a * SocketImplFactory set. */ + @Test public void testNewSocket11() throws IOException { var address = new InetSocketAddress("127.0.0.1", 8080); var httpProxy = new Proxy(Proxy.Type.HTTP, address); @@ -212,14 +229,15 @@ public void testNewSocket11() throws IOException { /** * Test creating a Socket no SocketImpl when there is a SocketImplFactory set. */ + @Test public void testNewSocket12() throws IOException { setSocketSocketImplFactory(() -> new CustomSocketImpl(false)); try { Socket s = new Socket((SocketImpl) null) { }; try (s) { - assertTrue(getSocketImpl(s) == null); + assertNull(getSocketImpl(s)); s.bind(loopbackSocketAddress()); // force SocketImpl to be created - assertTrue(getSocketImpl(s) instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, getSocketImpl(s)); } } finally { setSocketSocketImplFactory(null); @@ -230,6 +248,7 @@ public void testNewSocket12() throws IOException { * Test creating an unbound ServerSocket, it should be created with a platform * SocketImpl. */ + @Test public void testNewServerSocket1() throws IOException { try (ServerSocket ss = new ServerSocket()) { SocketImpl si = getSocketImpl(ss); @@ -241,6 +260,7 @@ public void testNewServerSocket1() throws IOException { * Test creating a bound ServerSocket, it should be created with a platform * SocketImpl. */ + @Test public void testNewServerSocket2() throws IOException { try (ServerSocket ss = new ServerSocket(0)) { SocketImpl si = getSocketImpl(ss); @@ -251,22 +271,24 @@ public void testNewServerSocket2() throws IOException { /** * Test creating a ServerSocket with a custom SocketImpl. */ + @Test public void testNewServerSocket3() throws IOException { ServerSocket ss = new ServerSocket(new CustomSocketImpl(true)) { }; try (ss) { SocketImpl si = getSocketImpl(ss); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } } /** * Test creating an unbound ServerSocket when there is a SocketImplFactory set. */ + @Test public void testNewServerSocket4() throws IOException { setServerSocketImplFactory(() -> new CustomSocketImpl(true)); try (ServerSocket ss = new ServerSocket()) { SocketImpl si = getSocketImpl(ss); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } finally { setServerSocketImplFactory(null); } @@ -275,11 +297,12 @@ public void testNewServerSocket4() throws IOException { /** * Test creating a bound ServerSocket when there is a SocketImplFactory set. */ + @Test public void testNewServerSocket5() throws IOException { setServerSocketImplFactory(() -> new CustomSocketImpl(true)); try (ServerSocket ss = new ServerSocket(0)) { SocketImpl si = getSocketImpl(ss); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); } finally { setServerSocketImplFactory(null); } @@ -289,13 +312,14 @@ public void testNewServerSocket5() throws IOException { * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl, * the Socket to accept is created with no SocketImpl. */ + @Test public void testServerSocketAccept1() throws IOException { var socket = new Socket((SocketImpl) null) { }; - assertTrue(getSocketImpl(socket) == null); + assertNull(getSocketImpl(socket)); serverSocketAccept(socket, (ss, s) -> { assertTrue(isPlatformSocketImpl(getSocketImpl(ss))); - assertTrue(s == socket); + assertSame(socket, s); SocketImpl si = getSocketImpl(s); assertTrue(isPlatformSocketImpl(si)); checkFields(si); @@ -307,13 +331,14 @@ public void testServerSocketAccept1() throws IOException { * the Socket to accept is created with no SocketImpl, and there is a custom * client SocketImplFactory set. */ + @Test public void testServerSocketAccept2() throws IOException { var socket = new Socket((SocketImpl) null) { }; - assertTrue(getSocketImpl(socket) == null); + assertNull(getSocketImpl(socket)); serverSocketAccept(socket, () -> new CustomSocketImpl(false), (ss, s) -> { assertTrue(isPlatformSocketImpl(getSocketImpl(ss))); - assertTrue(s == socket); + assertSame(socket, s); SocketImpl si = getSocketImpl(s); assertTrue(isPlatformSocketImpl(si)); checkFields(si); @@ -325,6 +350,7 @@ public void testServerSocketAccept2() throws IOException { * the Socket to accept is created with a SocketImpl that delegates to a * platform SocketImpl. */ + @Test public void testServerSocketAccept3() throws IOException { var socket = new Socket(); SocketImpl si = getSocketImpl(socket); @@ -334,7 +360,7 @@ public void testServerSocketAccept3() throws IOException { serverSocketAccept(socket, (ss, s) -> { assertTrue(isPlatformSocketImpl(getSocketImpl(ss))); - assertTrue(s == socket); + assertSame(socket, s); SocketImpl psi = getSocketImpl(socket); assertTrue(isPlatformSocketImpl(psi)); checkFields(psi); @@ -345,26 +371,28 @@ public void testServerSocketAccept3() throws IOException { * Test ServerSocket.accept. The ServerSocket uses a platform SocketImpl, * the Socket to accept is created with a custom SocketImpl. */ + @Test public void testServerSocketAccept4a() throws IOException { SocketImpl clientImpl = new CustomSocketImpl(false); Socket socket = new Socket(clientImpl) { }; - assertTrue(getSocketImpl(socket) == clientImpl); + assertSame(clientImpl, getSocketImpl(socket)); try (ServerSocket ss = serverSocketToAccept(socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { socket.close(); } } + @Test public void testServerSocketAccept4b() throws IOException { SocketImpl clientImpl = new CustomSocketImpl(false); Socket socket = new Socket(clientImpl) { }; - assertTrue(getSocketImpl(socket) == clientImpl); + assertSame(clientImpl, getSocketImpl(socket)); setSocketSocketImplFactory(() -> new CustomSocketImpl(false)); try (ServerSocket ss = serverSocketToAccept(socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { setSocketSocketImplFactory(null); socket.close(); @@ -375,42 +403,46 @@ public void testServerSocketAccept4b() throws IOException { * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl, * the Socket to accept is created no SocketImpl. */ + @Test public void testServerSocketAccept5a() throws IOException { SocketImpl serverImpl = new CustomSocketImpl(true); try (ServerSocket ss = new ServerSocket(serverImpl) { }) { ss.bind(loopbackSocketAddress()); - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } } + @Test public void testServerSocketAccept5b() throws IOException { var socket = new Socket((SocketImpl) null) { }; - assertTrue(getSocketImpl(socket) == null); + assertNull(getSocketImpl(socket)); SocketImpl serverImpl = new CustomSocketImpl(true); try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { socket.close(); } } + @Test public void testServerSocketAccept5c() throws IOException { setServerSocketImplFactory(() -> new CustomSocketImpl(true)); try (ServerSocket ss = new ServerSocket(0)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { setServerSocketImplFactory(null); } } + @Test public void testServerSocketAccept5d() throws IOException { var socket = new Socket((SocketImpl) null) { }; - assertTrue(getSocketImpl(socket) == null); + assertNull(getSocketImpl(socket)); setServerSocketImplFactory(() -> new CustomSocketImpl(true)); try (ServerSocket ss = serverSocketToAccept(socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { setServerSocketImplFactory(null); socket.close(); @@ -422,16 +454,17 @@ public void testServerSocketAccept5d() throws IOException { * the Socket to accept is created with no SocketImpl, and there is a custom * client SocketImplFactory set. */ + @Test public void testServerSocketAccept6() throws Exception { var socket = new Socket((SocketImpl) null) { }; - assertTrue(getSocketImpl(socket) == null); + assertNull(getSocketImpl(socket)); SocketImpl serverImpl = new CustomSocketImpl(true); SocketImplFactory clientFactory = () -> new CustomSocketImpl(false); serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> { - assertTrue(getSocketImpl(ss) == serverImpl); + assertSame(serverImpl, getSocketImpl(ss)); SocketImpl si = getSocketImpl(s); - assertTrue(si instanceof CustomSocketImpl); + assertInstanceOf(CustomSocketImpl.class, si); checkFields(si); }); } @@ -441,6 +474,7 @@ public void testServerSocketAccept6() throws Exception { * the Socket to accept is created with a SocketImpl that delegates to a * platform SocketImpl. */ + @Test public void testServerSocketAccept7a() throws IOException { var socket = new Socket(); SocketImpl si = getSocketImpl(socket); @@ -450,12 +484,13 @@ public void testServerSocketAccept7a() throws IOException { SocketImpl serverImpl = new CustomSocketImpl(true); try (ServerSocket ss = serverSocketToAccept(serverImpl, socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { socket.close(); } } + @Test public void testServerSocketAccept7b() throws IOException { var socket = new Socket(); SocketImpl si = getSocketImpl(socket); @@ -465,7 +500,7 @@ public void testServerSocketAccept7b() throws IOException { setServerSocketImplFactory(() -> new CustomSocketImpl(true)); try (ServerSocket ss = serverSocketToAccept(socket)) { - expectThrows(IOException.class, ss::accept); + assertThrows(IOException.class, ss::accept); } finally { setServerSocketImplFactory(null); socket.close(); @@ -476,16 +511,17 @@ public void testServerSocketAccept7b() throws IOException { * Test ServerSocket.accept. The ServerSocket uses a custom SocketImpl, * the Socket to accept is created with a custom SocketImpl. */ + @Test public void testServerSocketAccept8() throws Exception { SocketImpl clientImpl = new CustomSocketImpl(false); Socket socket = new Socket(clientImpl) { }; - assertTrue(getSocketImpl(socket) == clientImpl); + assertSame(clientImpl, getSocketImpl(socket)); SocketImpl serverImpl = new CustomSocketImpl(true); SocketImplFactory clientFactory = () -> new CustomSocketImpl(false); serverSocketAccept(serverImpl, socket, clientFactory, (ss, s) -> { - assertTrue(getSocketImpl(ss) == serverImpl); - assertTrue(getSocketImpl(s) == clientImpl); + assertSame(serverImpl, getSocketImpl(ss)); + assertSame(clientImpl, getSocketImpl(s)); checkFields(clientImpl); }); } @@ -751,11 +787,14 @@ static void checkFields(SocketImpl si) { InetAddress address = get(si, "address"); int port = get(si, "port"); int localport = get(si, "localport"); - assertTrue(fd.valid() && address != null && port != 0 && localport != 0); + assertTrue(fd.valid()); + assertNotNull(address); + assertNotEquals(0, port); + assertNotEquals(0, localport); } /** - * Custom SocketImpl that is layed on a SocketChannel or ServerSocketChannel + * Custom SocketImpl that is layered on a SocketChannel or ServerSocketChannel */ static class CustomSocketImpl extends SocketImpl { private final boolean server; diff --git a/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java b/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java index 6ee01a56a1e..c08d652270d 100644 --- a/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java +++ b/test/jdk/java/net/SocketImpl/TestDefaultBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @test * @bug 8224477 * @summary Basic test for java.net.SocketImpl default behavior - * @run testng TestDefaultBehavior + * @run junit ${test.main.class} */ import java.io.IOException; @@ -36,11 +36,13 @@ import java.net.SocketImpl; import java.net.SocketOption; import java.util.Set; -import org.testng.annotations.Test; import static java.lang.Boolean.*; import static java.net.StandardSocketOptions.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.expectThrows; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; public class TestDefaultBehavior { @@ -51,21 +53,21 @@ public class TestDefaultBehavior { public void socketImpl() { CustomSocketImpl csi = new CustomSocketImpl(); - assertEquals(csi.supportedOptions().size(), 0); - - expectThrows(NPE, () -> csi.setOption(null, null)); - expectThrows(NPE, () -> csi.setOption(null, 1)); - expectThrows(UOE, () -> csi.setOption(SO_RCVBUF, 100)); - expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE)); - expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, FALSE)); - expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, TRUE)); - expectThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, FALSE)); - expectThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE)); - - expectThrows(NPE, () -> csi.getOption(null)); - expectThrows(UOE, () -> csi.getOption(SO_RCVBUF)); - expectThrows(UOE, () -> csi.getOption(SO_KEEPALIVE)); - expectThrows(UOE, () -> csi.getOption(FAKE_SOCK_OPT)); + assertEquals(0, csi.supportedOptions().size()); + + assertThrows(NPE, () -> csi.setOption(null, null)); + assertThrows(NPE, () -> csi.setOption(null, 1)); + assertThrows(UOE, () -> csi.setOption(SO_RCVBUF, 100)); + assertThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE)); + assertThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, FALSE)); + assertThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, TRUE)); + assertThrows(UOE, () -> csi.setOption(FAKE_SOCK_OPT, FALSE)); + assertThrows(UOE, () -> csi.setOption(SO_KEEPALIVE, TRUE)); + + assertThrows(NPE, () -> csi.getOption(null)); + assertThrows(UOE, () -> csi.getOption(SO_RCVBUF)); + assertThrows(UOE, () -> csi.getOption(SO_KEEPALIVE)); + assertThrows(UOE, () -> csi.getOption(FAKE_SOCK_OPT)); } static final SocketOption FAKE_SOCK_OPT = new SocketOption<>() { diff --git a/test/jdk/java/net/SocketOption/ImmutableOptions.java b/test/jdk/java/net/SocketOption/ImmutableOptions.java index f15734edddc..372a0322a35 100644 --- a/test/jdk/java/net/SocketOption/ImmutableOptions.java +++ b/test/jdk/java/net/SocketOption/ImmutableOptions.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 @@ -33,18 +33,23 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.*; +import java.util.Optional; import java.util.Set; -import jdk.test.lib.net.IPSupport; - +import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; + public class ImmutableOptions { @BeforeTest void setupServerSocketFactory() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); ServerSocket.setSocketFactory(new ServerSocketImplFactory()); } diff --git a/test/jdk/java/net/httpclient/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index df808ad2dab..dcb60a55061 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 + * @bug 8245462 8229822 8254786 8297075 8297149 8298340 8302635 8377181 * @summary Tests cancelling the request. * @library /test/lib /test/jdk/java/net/httpclient/lib * @key randomness diff --git a/test/jdk/java/net/httpclient/InvalidSSLContextTest.java b/test/jdk/java/net/httpclient/InvalidSSLContextTest.java index 0027a7956d5..bbbd1e486ca 100644 --- a/test/jdk/java/net/httpclient/InvalidSSLContextTest.java +++ b/test/jdk/java/net/httpclient/InvalidSSLContextTest.java @@ -23,10 +23,12 @@ /* * @test + * @bug 8381316 * @summary Test to ensure the HTTP client throws an appropriate SSL exception * when SSL context is not valid. - * @library /test/lib + * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext + * jdk.httpclient.test.lib.common.HttpServerAdapters * @run junit/othervm -Djdk.internal.httpclient.debug=true InvalidSSLContextTest */ @@ -44,14 +46,18 @@ import javax.net.ssl.SSLSocket; import java.net.http.HttpClient; import java.net.http.HttpClient.Version; +import java.net.http.HttpOption; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; + +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; import jdk.test.lib.net.SimpleSSLContext; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static java.net.http.HttpClient.Version.HTTP_1_1; -import static java.net.http.HttpClient.Version.HTTP_2; +import static java.net.http.HttpClient.Version.*; +import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; +import static jdk.httpclient.test.lib.common.HttpServerAdapters.createClientBuilderForH3; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -64,25 +70,29 @@ public class InvalidSSLContextTest { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); static volatile SSLServerSocket sslServerSocket; static volatile String uri; + private static HttpTestServer h3Server; + private static String h3Uri; public static Object[][] versions() { return new Object[][]{ - { HTTP_1_1 }, - { HTTP_2 } + { HTTP_1_1, uri }, + { HTTP_2 , uri }, + { HTTP_3 , h3Uri } }; } @ParameterizedTest @MethodSource("versions") - public void testSync(Version version) throws Exception { + public void testSync(Version version, String uri) throws Exception { // client-side uses a different context to that of the server-side - HttpClient client = HttpClient.newBuilder() + HttpClient client = createClientBuilderForH3() .proxy(NO_PROXY) .sslContext(SSLContext.getDefault()) .build(); HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) .version(version) + .setOption(HttpOption.H3_DISCOVERY, HTTP_3_URI_ONLY) .build(); try { @@ -90,21 +100,22 @@ public void testSync(Version version) throws Exception { Assertions.fail("UNEXPECTED response" + response); } catch (IOException ex) { System.out.println("Caught expected: " + ex); - assertExceptionOrCause(SSLException.class, ex); + assertException(SSLException.class, ex); } } @ParameterizedTest @MethodSource("versions") - public void testAsync(Version version) throws Exception { + public void testAsync(Version version, String uri) throws Exception { // client-side uses a different context to that of the server-side - HttpClient client = HttpClient.newBuilder() + HttpClient client = createClientBuilderForH3() .proxy(NO_PROXY) .sslContext(SSLContext.getDefault()) .build(); HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) .version(version) + .setOption(HttpOption.H3_DISCOVERY, HTTP_3_URI_ONLY) .build(); assertExceptionally(SSLException.class, @@ -123,26 +134,20 @@ static void assertExceptionally(Class clazz, if (cause == null) { Assertions.fail("Unexpected null cause: " + error); } - assertExceptionOrCause(clazz, cause); + System.out.println("Caught expected: " + cause); + assertException(clazz, cause); } else { - assertExceptionOrCause(clazz, error); + System.out.println("Caught expected: " + error); + assertException(clazz, error); } return null; }).join(); } - static void assertExceptionOrCause(Class clazz, Throwable t) { - if (t == null) { - Assertions.fail("Expected " + clazz + ", caught nothing"); - } - final Throwable original = t; - do { - if (clazz.isInstance(t)) { - return; // found - } - } while ((t = t.getCause()) != null); - original.printStackTrace(System.out); - Assertions.fail("Expected " + clazz + "in " + original); + static void assertException(Class clazz, Throwable t) { + Assertions.assertInstanceOf(clazz, t); + Assertions.assertTrue(t.getMessage().contains("unable to find valid certification path to requested target"), + "Unexpected exception message: " + t); } @BeforeAll @@ -159,7 +164,7 @@ public static void setup() throws Exception { Thread t = new Thread("SSL-Server-Side") { @Override public void run() { - while (true) { + while (!sslServerSocket.isClosed()) { try { SSLSocket s = (SSLSocket) sslServerSocket.accept(); System.out.println("SERVER: accepted: " + s); @@ -177,7 +182,6 @@ public void run() { if (!sslServerSocket.isClosed()) { throw new UncheckedIOException(e); } - break; } catch (InterruptedException ie) { throw new RuntimeException(ie); } @@ -185,10 +189,16 @@ public void run() { } }; t.start(); + + h3Server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); + h3Server.addHandler((exchange) -> exchange.sendResponseHeaders(200, 0), "/hello"); + h3Server.start(); + h3Uri = "https://" + h3Server.serverAuthority() + "/hello"; } @AfterAll public static void teardown() throws Exception { + h3Server.stop(); sslServerSocket.close(); } } diff --git a/test/jdk/java/net/httpclient/http3/H3ErrorHandlingTest.java b/test/jdk/java/net/httpclient/http3/H3ErrorHandlingTest.java index 8dfef7417e3..8e20eac95fa 100644 --- a/test/jdk/java/net/httpclient/http3/H3ErrorHandlingTest.java +++ b/test/jdk/java/net/httpclient/http3/H3ErrorHandlingTest.java @@ -71,7 +71,7 @@ /* * @test - * @bug 8373409 + * @bug 8373409 8377181 * @key intermittent * @comment testResetControlStream may fail if the client doesn't read the stream type * before the stream is reset, diff --git a/test/jdk/java/net/httpclient/quic/VariableLengthTest.java b/test/jdk/java/net/httpclient/quic/VariableLengthTest.java index cf05b91b18f..6d805fbad5a 100644 --- a/test/jdk/java/net/httpclient/quic/VariableLengthTest.java +++ b/test/jdk/java/net/httpclient/quic/VariableLengthTest.java @@ -21,7 +21,6 @@ * questions. */ import jdk.internal.net.http.quic.VariableLengthEncoder; -import jtreg.SkippedException; import java.io.IOException; import java.nio.ByteBuffer; @@ -31,9 +30,9 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.opentest4j.TestAbortedException; /* * @test @@ -345,7 +344,7 @@ private ByteBuffer getTestBuffer(long length, int capacity) { case 2 -> ByteBuffer.allocate(capacity).putShort((short) length); case 4 -> ByteBuffer.allocate(capacity).putInt((int) length); case 8 -> ByteBuffer.allocate(capacity).putLong(length); - default -> throw new SkippedException("bad value used for capacity"); + default -> throw new TestAbortedException("bad value used for capacity"); }; } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java index 26dabc00063..f4fa2016983 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesCachingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,13 @@ import java.net.InetAddress; import java.net.UnknownHostException; -import org.testng.Assert; -import org.testng.annotations.Test; import impl.SimpleResolverProviderImpl; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /* @@ -37,26 +40,26 @@ * @library lib providers/simple * @build test.library/testlib.ResolutionRegistry * simple.provider/impl.SimpleResolverProviderImpl AddressesCachingTest - * @run testng/othervm -Djava.security.properties=${test.src}/props/NeverCache.props + * @run junit/othervm -Djava.security.properties=${test.src}/props/NeverCache.props * -Dtest.cachingDisabled=true AddressesCachingTest - * @run testng/othervm -Djava.security.properties=${test.src}/props/ForeverCache.props + * @run junit/othervm -Djava.security.properties=${test.src}/props/ForeverCache.props * -Dtest.cachingDisabled=false AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/NeverCacheIgnoreMinusStale.props * -Dtest.cachingDisabled=true AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/NeverCacheIgnorePositiveStale.props * -Dtest.cachingDisabled=true AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/NeverCacheIgnoreZeroStale.props * -Dtest.cachingDisabled=true AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreMinusStale.props * -Dtest.cachingDisabled=false AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/ForeverCacheIgnorePositiveStale.props * -Dtest.cachingDisabled=false AddressesCachingTest - * @run testng/othervm + * @run junit/othervm * -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreZeroStale.props * -Dtest.cachingDisabled=false AddressesCachingTest */ @@ -66,10 +69,10 @@ public class AddressesCachingTest { public void testPositiveCaching() { boolean observedTwoLookups = performLookups(false); if (CACHING_DISABLED) { - Assert.assertTrue(observedTwoLookups, + assertTrue(observedTwoLookups, "Two positive lookups are expected with caching disabled"); } else { - Assert.assertFalse(observedTwoLookups, + assertFalse(observedTwoLookups, "Only one positive lookup is expected with caching enabled"); } } @@ -78,10 +81,10 @@ public void testPositiveCaching() { public void testNegativeCaching() { boolean observedTwoLookups = performLookups(true); if (CACHING_DISABLED) { - Assert.assertTrue(observedTwoLookups, + assertTrue(observedTwoLookups, "Two negative lookups are expected with caching disabled"); } else { - Assert.assertFalse(observedTwoLookups, + assertFalse(observedTwoLookups, "Only one negative lookup is expected with caching enabled"); } } @@ -107,11 +110,11 @@ private static void doLookup(boolean performNegativeLookup) { try { InetAddress.getByName(hostName); if (performNegativeLookup) { - Assert.fail("Host name is expected to get unresolved"); + fail("Host name is expected to get unresolved"); } } catch (UnknownHostException uhe) { if (!performNegativeLookup) { - Assert.fail("Host name is expected to get resolved"); + fail("Host name is expected to get resolved"); } } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java index a8393221a72..a2c5c3e8443 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/AddressesStaleCachingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,11 @@ import java.util.concurrent.TimeUnit; import impl.SimpleResolverProviderImpl; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; /* @@ -38,7 +41,7 @@ * @library lib providers/simple * @build test.library/testlib.ResolutionRegistry * simple.provider/impl.SimpleResolverProviderImpl AddressesStaleCachingTest - * @run testng/othervm -Djava.security.properties=${test.src}/props/CacheStale.props AddressesStaleCachingTest + * @run junit/othervm -Djava.security.properties=${test.src}/props/CacheStale.props AddressesStaleCachingTest */ public class AddressesStaleCachingTest { @@ -64,20 +67,21 @@ public void testRefresh() throws Exception{ Thread.sleep(10000); // intentionally big delay > x2 stale property // The refreshTime is expired, we will do the successful lookup. Lookup second = doLookup(false, 0); - Assert.assertNotEquals(first.timestamp, second.timestamp, + assertNotEquals(first.timestamp, second.timestamp, "Two lookups are expected"); Thread.sleep(10000); // intentionally big delay > x2 stale property // The refreshTime is expired again, we will do the failed lookup. Lookup third = doLookup(true, 0); - Assert.assertNotEquals(second.timestamp, third.timestamp, + assertNotEquals(second.timestamp, third.timestamp, "Two lookups are expected"); // The stale cache is enabled, so we should get valid/same data for // all requests(even for the failed request). - Assert.assertEquals(first.address, second.address, + assertArrayEquals(first.address, second.address, "Same address is expected"); - Assert.assertEquals(second.address, third.address, + + assertArrayEquals(second.address, third.address, "Same address is expected"); } @@ -133,10 +137,10 @@ private static Lookup doLookup(boolean error, long timeout) { byte[] secondAddress = InetAddress.getByName("javaTest.org").getAddress(); long secondTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp(); - Assert.assertEquals(firstAddress, secondAddress, + assertArrayEquals(firstAddress, secondAddress, "Same address is expected"); if (timeout == 0 || timeout - System.nanoTime() > 0) { - Assert.assertEquals(firstTimestamp, secondTimestamp, + assertEquals(firstTimestamp, secondTimestamp, "Only one positive lookup is expected with caching enabled"); } return new Lookup(firstAddress, firstTimestamp); diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/BootstrapResolverUsageTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/BootstrapResolverUsageTest.java index 9f0902d2c1d..012410c1566 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/BootstrapResolverUsageTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/BootstrapResolverUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,12 +21,13 @@ * questions. */ +import org.junit.jupiter.api.Test; + import java.net.InetAddress; -import org.testng.Assert; -import org.testng.annotations.Test; import static impl.WithBootstrapResolverUsageProvider.numberOfGetCalls; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test @@ -35,7 +36,7 @@ * InetAddressResolverProvider.get method uses InetAddress lookup API. * @library providers/bootstrapUsage * @build bootstrap.usage.provider/impl.WithBootstrapResolverUsageProvider - * @run testng/othervm BootstrapResolverUsageTest + * @run junit/othervm BootstrapResolverUsageTest */ public class BootstrapResolverUsageTest { @@ -43,7 +44,6 @@ public class BootstrapResolverUsageTest { @Test public void testSuccessfulProviderInstantiationTest() throws Exception { System.err.println(InetAddress.getAllByName(InetAddress.getLocalHost().getHostName())); - Assert.assertEquals(numberOfGetCalls, 1, - "InetAddressResolverProvider.get was called more than once"); + assertEquals(1, numberOfGetCalls, "InetAddressResolverProvider.get was called more than once"); } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/BuiltInResolverTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/BuiltInResolverTest.java index 8b8b866d7b2..e91446e540a 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/BuiltInResolverTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/BuiltInResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,30 +21,34 @@ * questions. */ -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.net.spi.InetAddressResolver; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* * @test * @summary white-box test to check that the built-in resolver * is used by default. * @modules java.base/java.net:open - * @run testng/othervm BuiltInResolverTest + * @run junit/othervm BuiltInResolverTest */ public class BuiltInResolverTest { - private Field builtInResolverField, resolverField; + private static Field builtInResolverField, resolverField; - @BeforeTest - public void beforeTest() throws NoSuchFieldException { + @BeforeAll + public static void beforeTest() throws NoSuchFieldException { Class inetAddressClass = InetAddress.class; // Needs to happen for InetAddress.resolver to be initialized try { @@ -72,7 +76,7 @@ public void testDefaultNSContext() throws IllegalAccessException { assertNotNull(defaultClassName, "defaultClassName not set"); assertNotNull(currentClassName, "currentClassName name not set"); - assertEquals(currentClassName, defaultClassName, + assertEquals(defaultClassName, currentClassName, "BUILTIN_RESOLVER resolver was not used."); System.err.println("Resolver used by default is the built-in resolver"); } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/EmptyResultsStreamTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/EmptyResultsStreamTest.java index 17b9aa5d011..db0f293b712 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/EmptyResultsStreamTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/EmptyResultsStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,32 +21,36 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @summary checks that InetAddress forward lookup API throw UnknownHostException * when resolver returns empty address stream. * @library providers/empty * @build empty.results.provider/impl.EmptyResultsProviderImpl - * @run testng/othervm EmptyResultsStreamTest + * @run junit/othervm EmptyResultsStreamTest */ public class EmptyResultsStreamTest { - @Test(expectedExceptions = UnknownHostException.class) - public void getAllByNameTest() throws UnknownHostException { - System.err.println("getAllByName unexpectedly completed: " + - Arrays.deepToString(InetAddress.getAllByName("test1.org"))); + @Test() + public void getAllByNameTest() { + assertThrows(UnknownHostException.class, () -> { + System.err.println("getAllByName unexpectedly completed: " + + Arrays.deepToString(InetAddress.getAllByName("test1.org"))); + }); } - @Test(expectedExceptions = UnknownHostException.class) - public void getByNameTest() throws UnknownHostException { - System.err.println("getByName unexpectedly completed: " + - InetAddress.getByName("test2.org")); + @Test() + public void getByNameTest() { + assertThrows(UnknownHostException.class, () -> { + System.err.println("getByName unexpectedly completed: " + + InetAddress.getByName("test2.org")); + }); } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/InetAddressUsageInGetProviderTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/InetAddressUsageInGetProviderTest.java index dd52893c1e8..3458270960b 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/InetAddressUsageInGetProviderTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/InetAddressUsageInGetProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,7 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import java.net.InetAddress; @@ -32,7 +31,7 @@ * wouldn't cause stack overflow and will be successfully installed. * @library providers/recursive * @build recursive.init.provider/impl.InetAddressUsageInGetProviderImpl - * @run testng/othervm InetAddressUsageInGetProviderTest + * @run junit/othervm InetAddressUsageInGetProviderTest */ public class InetAddressUsageInGetProviderTest { diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyMappingTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyMappingTest.java index e845e2e0461..13ca6e50aaf 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyMappingTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyMappingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,12 +31,15 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV4_FIRST; import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6; import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import jdk.test.lib.net.IPSupport; import jdk.test.lib.NetworkConfiguration; -import org.testng.annotations.Test; -import org.testng.Assert; -import org.testng.SkipException; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.abort; /* * @test @@ -45,26 +48,26 @@ * @library lib providers/simple /test/lib * @build test.library/testlib.ResolutionRegistry simple.provider/impl.SimpleResolverProviderImpl * jdk.test.lib.net.IPSupport LookupPolicyMappingTest - * @run testng/othervm LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=true LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack=false LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv4Stack LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest - * @run testng/othervm -Djava.net.preferIPv6Addresses LookupPolicyMappingTest + * @run junit/othervm LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=true LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6Addresses LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack=false LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack -Djava.net.preferIPv6Addresses LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv4Stack LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv6Addresses=true LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv6Addresses=false LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv6Addresses=system LookupPolicyMappingTest + * @run junit/othervm -Djava.net.preferIPv6Addresses LookupPolicyMappingTest */ public class LookupPolicyMappingTest { @@ -88,19 +91,19 @@ public void testSystemProperties() throws Exception { String expectedResultsKey = calculateMapKey(preferIPv4Stack, preferIPv6Addresses); int expectedCharacteristics = EXPECTED_RESULTS_MAP.get(expectedResultsKey); - Assert.assertTrue(characteristicsMatch( + assertTrue(characteristicsMatch( runtimeCharacteristics, expectedCharacteristics), "Unexpected LookupPolicy observed"); } // Throws SkipException if platform doesn't support required IP address types static void checkPlatformNetworkConfiguration() { - IPSupport.throwSkippedExceptionIfNonOperational(); + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); IPSupport.printPlatformSupport(System.err); NetworkConfiguration.printSystemConfiguration(System.err); // If preferIPv4=true and no IPv4 - skip if (IPSupport.preferIPv4Stack()) { if (!IPSupport.hasIPv4()) { - throw new SkipException("Skip tests - IPv4 support required"); + abort("Skip tests - IPv4 support required"); } return; } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyOfTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyOfTest.java index 73e2581f5cd..09585322c83 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyOfTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/LookupPolicyOfTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,11 @@ * @test * @summary check if LookupPolicy.of correctly handles valid and illegal * combinations of characteristics bit mask flags. - * @run testng LookupPolicyOfTest + * @run junit LookupPolicyOfTest */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.net.spi.InetAddressResolver.LookupPolicy; import java.util.List; @@ -38,21 +38,23 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV4_FIRST; import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6; import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST; +import static org.junit.jupiter.api.Assertions.assertThrows; public class LookupPolicyOfTest { - @Test(dataProvider = "validCharacteristics") + @ParameterizedTest + @MethodSource("validCharacteristicValue") public void testValidCharacteristicCombinations(List validCombination) { LookupPolicy.of(bitFlagsToCharacteristicsValue(validCombination)); } - @Test(dataProvider = "invalidCharacteristics", expectedExceptions = IllegalArgumentException.class) - public void testInvalidCharacteristicCombinations(List invalidCombination) { - LookupPolicy.of(bitFlagsToCharacteristicsValue(invalidCombination)); + @ParameterizedTest + @MethodSource("illegalCharacteristicValue") + public void testInvalidCharacteristicCombination(List invalidCombination) { + assertThrows(IllegalArgumentException.class, () -> LookupPolicy.of(bitFlagsToCharacteristicsValue(invalidCombination))); } - @DataProvider(name = "validCharacteristics") - public Object[][] validCharacteristicValue() { + public static Object[][] validCharacteristicValue() { return new Object[][]{ {List.of(IPV4)}, {List.of(IPV4, IPV4_FIRST)}, @@ -68,8 +70,7 @@ public Object[][] validCharacteristicValue() { }; } - @DataProvider(name = "invalidCharacteristics") - public Object[][] illegalCharacteristicValue() { + public static Object[][] illegalCharacteristicValue() { return new Object[][]{ {List.of()}, {List.of(IPV4_FIRST)}, diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/ProviderGetExceptionTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/ProviderGetExceptionTest.java index fe5269c7b46..7df304bc235 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/ProviderGetExceptionTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/ProviderGetExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,13 +21,15 @@ * questions. */ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import java.net.InetAddress; import java.util.Arrays; -import org.testng.Assert; -import org.testng.annotations.Test; - import static impl.FaultyResolverProviderGetImpl.EXCEPTION_MESSAGE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test @@ -35,7 +37,7 @@ * instantiate a resolver. * @library providers/faulty * @build faulty.provider/impl.FaultyResolverProviderGetImpl - * @run testng/othervm ProviderGetExceptionTest + * @run junit/othervm ProviderGetExceptionTest */ public class ProviderGetExceptionTest { @@ -54,10 +56,10 @@ public void getByAddressExceptionTest() { callInetAddressAndCheckException(() -> InetAddress.getByAddress(address).getHostName()); } - private void callInetAddressAndCheckException(Assert.ThrowingRunnable apiCall) { - IllegalArgumentException iae = Assert.expectThrows(IllegalArgumentException.class, apiCall); + private void callInetAddressAndCheckException(Executable apiCall) { + IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, apiCall); System.out.println("Got exception of expected type:" + iae); - Assert.assertNull(iae.getCause(), "cause is not null"); - Assert.assertEquals(iae.getMessage(), EXCEPTION_MESSAGE); + assertNull(iae.getCause(), "cause is not null"); + assertEquals(EXCEPTION_MESSAGE, iae.getMessage()); } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/ResolutionWithExceptionTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/ResolutionWithExceptionTest.java index dd17c1d987c..ce68f8a8e6f 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/ResolutionWithExceptionTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/ResolutionWithExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,11 @@ import impl.ThrowingLookupsProviderImpl; import static impl.ThrowingLookupsProviderImpl.RUNTIME_EXCEPTION_MESSAGE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /* * @test @@ -37,7 +39,7 @@ * implementation throws RuntimeException during forward or reverse lookup. * @library providers/throwing * @build throwing.lookups.provider/impl.ThrowingLookupsProviderImpl - * @run testng/othervm ResolutionWithExceptionTest + * @run junit/othervm ResolutionWithExceptionTest */ public class ResolutionWithExceptionTest { @@ -68,7 +70,7 @@ public void getByAddressRuntimeException() throws UnknownHostException { private void runGetByNameTest() { // InetAddress.getByName() is expected to throw UnknownHostException in all cases - UnknownHostException uhe = Assert.expectThrows(UnknownHostException.class, + UnknownHostException uhe = Assertions.assertThrows(UnknownHostException.class, () -> InetAddress.getByName("doesnt.matter.com")); // If provider is expected to throw RuntimeException - check that UnknownHostException // is set as its cause @@ -76,10 +78,10 @@ private void runGetByNameTest() { Throwable cause = uhe.getCause(); if (cause instanceof RuntimeException re) { // Check RuntimeException message - Assert.assertEquals(re.getMessage(), RUNTIME_EXCEPTION_MESSAGE, + assertEquals(RUNTIME_EXCEPTION_MESSAGE, re.getMessage(), "incorrect exception message"); } else { - Assert.fail("UnknownHostException cause is not RuntimeException"); + fail("UnknownHostException cause is not RuntimeException"); } } } @@ -89,6 +91,6 @@ private void runGetByAddressTest() throws UnknownHostException { // if there is an error during reverse lookup operation the literal IP // address String will be returned. String literalIP = InetAddress.getByAddress(new byte[]{1, 2, 3, 4}).getCanonicalHostName(); - Assert.assertEquals(literalIP, "1.2.3.4"); + assertEquals("1.2.3.4", literalIP); } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/ReverseLookupDelegationTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/ReverseLookupDelegationTest.java index 439521736db..d72a6b1fc0e 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/ReverseLookupDelegationTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/ReverseLookupDelegationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,9 @@ * questions. */ -import impl.DelegatingProviderImpl; -import org.testng.Assert; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.UnknownHostException; @@ -37,7 +37,7 @@ * InetAddressResolver. * @library providers/delegating * @build delegating.provider/impl.DelegatingProviderImpl - * @run testng/othervm ReverseLookupDelegationTest + * @run junit/othervm ReverseLookupDelegationTest */ public class ReverseLookupDelegationTest { @@ -53,10 +53,10 @@ public void delegateHostNameLookupWithWrongByteArray() throws UnknownHostExcepti // Check that originally supplied byte array was used to construct canonical host name after // failed reverse lookup. - Assert.assertEquals("1.2.3.4", canonicalHostName, "unexpected canonical hostname"); + assertEquals("1.2.3.4", canonicalHostName, "unexpected canonical hostname"); // Check that on a provider side the IllegalArgumentException has been thrown by the built-in resolver - Assert.assertTrue(lastReverseLookupThrowable instanceof IllegalArgumentException, + assertTrue(lastReverseLookupThrowable instanceof IllegalArgumentException, "wrong exception type is thrown by the built-in resolver"); } } diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/classpath/ClasspathProviderTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/classpath/ClasspathProviderTest.java index 51285fb045c..c82f2442831 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/classpath/ClasspathProviderTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/classpath/ClasspathProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,18 +21,17 @@ * questions. */ -import java.net.InetAddress; +import org.junit.jupiter.api.Test; -import org.testng.annotations.Test; +import java.net.InetAddress; -import static org.testng.Assert.assertThrows; /* * @test * @summary Test that InetAddressResolverProvider implementation can be installed to a class path. * @library ../../lib * @build test.library/testlib.ResolutionRegistry ClasspathResolverProviderImpl - * @run testng/othervm ClasspathProviderTest + * @run junit/othervm ClasspathProviderTest */ public class ClasspathProviderTest { diff --git a/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/module/ModularProviderTest.java b/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/module/ModularProviderTest.java index 410f25aa9c3..56e52d52729 100644 --- a/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/module/ModularProviderTest.java +++ b/test/jdk/java/net/spi/InetAddressResolverProvider/serviceProviderOriginType/module/ModularProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,10 @@ * questions. */ +import org.junit.jupiter.api.Test; + import java.net.InetAddress; -import org.testng.annotations.Test; /* * @test @@ -31,7 +32,7 @@ * @library ../../lib ../../providers/simple * @build test.library/testlib.ResolutionRegistry simple.provider/impl.SimpleResolverProviderImpl * ModularProviderTest - * @run testng/othervm ModularProviderTest + * @run junit/othervm ModularProviderTest */ diff --git a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java index 44a34ea3e55..285e98c0fc6 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java @@ -49,8 +49,11 @@ import java.util.Map; import java.util.function.Predicate; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; + import jdk.test.lib.net.IPSupport; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import static java.nio.charset.StandardCharsets.UTF_8; @@ -92,7 +95,8 @@ static class BindExceptionOnDisconnect extends BindException { @Test public void execute() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); + boolean preferIPv6 = Boolean.getBoolean("java.net.preferIPv6Addresses"); InetAddress lb = InetAddress.getLoopbackAddress(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index cb6e66dd5de..7d41382cf65 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -83,14 +83,14 @@ import static java.net.StandardProtocolFamily.INET6; import static java.net.StandardSocketOptions.SO_SNDBUF; import static java.net.StandardSocketOptions.SO_RCVBUF; -import static jdk.test.lib.net.IPSupport.hasIPv4; -import static jdk.test.lib.net.IPSupport.hasIPv6; -import static jdk.test.lib.net.IPSupport.preferIPv4Stack; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.Assumptions; + +import static jdk.test.lib.net.IPSupport.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -107,7 +107,7 @@ public interface DatagramChannelSupplier { @BeforeAll public static void setUp() { - IPSupport.throwSkippedExceptionIfNonOperational(); + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); } public static List testCases() throws IOException { diff --git a/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java index 60e41af6288..7439c7e436b 100644 --- a/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java +++ b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java @@ -42,9 +42,9 @@ import java.util.stream.Stream; import static java.lang.System.out; + import static jdk.test.lib.net.IPSupport.*; -import jtreg.SkippedException; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; @@ -56,12 +56,7 @@ public class LocalSocketAddressType { @BeforeAll() public static void setup() { IPSupport.printPlatformSupport(out); - try { - throwSkippedExceptionIfNonOperational(); - } catch (SkippedException skippedException) { - // jtreg.SkippedException would cause a JUnit test to fail - Assumptions.assumeTrue(false, skippedException.getMessage()); - } + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); } public static Iterator addresses() throws Exception { diff --git a/test/jdk/java/nio/channels/etc/OpenAndConnect.java b/test/jdk/java/nio/channels/etc/OpenAndConnect.java index 0e0347ebb57..ffdbebcc9a5 100644 --- a/test/jdk/java/nio/channels/etc/OpenAndConnect.java +++ b/test/jdk/java/nio/channels/etc/OpenAndConnect.java @@ -42,7 +42,6 @@ import static java.net.StandardProtocolFamily.INET6; import static jdk.test.lib.net.IPSupport.*; -import jtreg.SkippedException; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; @@ -89,12 +88,7 @@ public class OpenAndConnect { public static void setup() { NetworkConfiguration.printSystemConfiguration(out); IPSupport.printPlatformSupport(out); - try { - throwSkippedExceptionIfNonOperational(); - } catch (SkippedException skippedException) { - // jtreg.SkippedException would cause a JUnit test to fail - Assumptions.assumeTrue(false, skippedException.getMessage()); - } + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); out.println("IA4LOCAL: " + IA4LOCAL); out.println("IA6LOCAL: " + IA6LOCAL); diff --git a/test/jdk/java/nio/channels/etc/ProtocolFamilies.java b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java index 7f823ff35a8..2419cdd73cf 100644 --- a/test/jdk/java/nio/channels/etc/ProtocolFamilies.java +++ b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java @@ -45,7 +45,6 @@ import static java.net.StandardProtocolFamily.INET6; import static jdk.test.lib.net.IPSupport.*; -import jtreg.SkippedException; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -77,12 +76,7 @@ public class ProtocolFamilies { public static void setup() throws Exception { NetworkConfiguration.printSystemConfiguration(out); IPSupport.printPlatformSupport(out); - try { - throwSkippedExceptionIfNonOperational(); - } catch (SkippedException skippedException) { - // jtreg.SkippedException would cause a JUnit test to fail - Assumptions.assumeTrue(false, skippedException.getMessage()); - } + diagnoseConfigurationIssue().ifPresent(Assumptions::abort); ia4 = getLocalIPv4Address(); ia6 = getLocalIPv6Address(); diff --git a/test/jdk/java/text/Format/DecimalFormat/CloneTest.java b/test/jdk/java/text/Format/DecimalFormat/CloneTest.java index dabdd137f5f..1a6821777be 100644 --- a/test/jdk/java/text/Format/DecimalFormat/CloneTest.java +++ b/test/jdk/java/text/Format/DecimalFormat/CloneTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,8 +29,8 @@ * @run junit/othervm --add-opens=java.base/java.text=ALL-UNNAMED CloneTest */ -import jtreg.SkippedException; import org.junit.jupiter.api.Test; +import org.opentest4j.TestAbortedException; import java.lang.reflect.Field; import java.math.BigDecimal; @@ -78,7 +78,7 @@ public CloneTester(DecimalFormat original) { digitListClass = digitList.getClass(); } catch (ReflectiveOperationException e) { - throw new SkippedException("reflective access in white-box test failed", e); + throw new TestAbortedException("reflective access in white-box test failed", e); } } @@ -91,7 +91,7 @@ public void testClone() { assertEquals(digitListField.get(original), digitListField.get(dfClone)); } catch (ReflectiveOperationException e) { - throw new SkippedException("reflective access in white-box test failed", e); + throw new TestAbortedException("reflective access in white-box test failed", e); } } diff --git a/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryFilesRescan.java b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryFilesRescan.java new file mode 100644 index 00000000000..0cb80d18698 --- /dev/null +++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestRepositoryFilesRescan.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.streaming; + +import java.nio.file.Files; +import java.nio.file.Path; + +import jdk.jfr.Recording; +import jdk.jfr.internal.consumer.RepositoryFiles; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Verifies that RepositoryFiles does not oscillate between + * forgetting and rediscovering chunk files between scans + * @requires vm.hasJFR + * @library /test/lib + * @modules jdk.jfr/jdk.jfr.internal.consumer + * @run main/othervm jdk.jfr.api.consumer.streaming.TestRepositoryFilesRescan + */ +public class TestRepositoryFilesRescan { + + public static void main(String... args) throws Exception { + Path dir = Files.createTempDirectory("jfr-rescan-test"); + try { + createChunkFile(dir, "chunk1.jfr"); + createChunkFile(dir, "chunk2.jfr"); + testNoOscillation(dir); + } finally { + deleteDirectory(dir); + } + } + + /** + * firstPath(0, false) will return non-null only when updatePaths discovers + * genuinely new chunk files; with no files added or removed between + * calls the result must be null after the first successful call. + */ + private static void testNoOscillation(Path dir) { + RepositoryFiles rf = new RepositoryFiles(dir, false); + Asserts.assertNotNull(rf.firstPath(0, false), "Call 1: expected non-null (initial discovery of .jfr files)"); + + Path p2 = rf.firstPath(0, false); + Asserts.assertNull(p2, "Call 2: expected null (no new chunks), got " + p2); + + // Call 3: still no new files. This confirms call 2 did not wipe pathLookup, making all files look "new" again. + Path p3 = rf.firstPath(0, false); + Asserts.assertNull(p3, "Call 3: expected null (no new chunks), got " + p3 + + " — pathLookup is oscillating between populated and empty"); + } + + private static void createChunkFile(Path dir, String name) throws Exception { + try (Recording r = new Recording()) { + r.start(); + Thread.sleep(20); + r.stop(); + r.dump(dir.resolve(name)); + } + } + + private static void deleteDirectory(Path dir) { + try { + try (var stream = Files.list(dir)) { + stream.forEach(p -> { + try { + Files.deleteIfExists(p); + } catch (Exception e) { + // Do nothing + } + }); + } + Files.deleteIfExists(dir); + } catch (Exception e) { + // best-effort cleanup + } + } +} diff --git a/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java b/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java index 2baf87eae31..178d796e1b0 100644 --- a/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java +++ b/test/jdk/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ public static void test(String expectedYoungCollector, String expectedOldCollect GCHelper.callSystemGc(5, true); recording.stop(); - List allEvents = Events.fromRecording(recording); + List allEvents = Events.fromRecordingOrdered(recording); if (!checkCollectors(allEvents, expectedYoungCollector, expectedOldCollector)) { return; } diff --git a/test/jdk/jdk/net/ExtendedSocketOption/AsynchronousSocketChannelNAPITest.java b/test/jdk/jdk/net/ExtendedSocketOption/AsynchronousSocketChannelNAPITest.java index d5994ce0add..7fc86ff9f8f 100644 --- a/test/jdk/jdk/net/ExtendedSocketOption/AsynchronousSocketChannelNAPITest.java +++ b/test/jdk/jdk/net/ExtendedSocketOption/AsynchronousSocketChannelNAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * @run testng/othervm -Djava.net.preferIPv4Stack=true AsynchronousSocketChannelNAPITest */ -import jdk.test.lib.net.IPSupport; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -44,7 +43,9 @@ import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; +import java.util.Optional; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; @@ -59,7 +60,10 @@ public class AsynchronousSocketChannelNAPITest { @BeforeTest public void setup() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); try (var sc = AsynchronousSocketChannel.open(); var ssc = AsynchronousServerSocketChannel.open()) { if (!sc.supportedOptions().contains(SO_INCOMING_NAPI_ID)) { diff --git a/test/jdk/jdk/net/ExtendedSocketOption/DatagramChannelNAPITest.java b/test/jdk/jdk/net/ExtendedSocketOption/DatagramChannelNAPITest.java index 24623e52cf1..d38b0c5da88 100644 --- a/test/jdk/jdk/net/ExtendedSocketOption/DatagramChannelNAPITest.java +++ b/test/jdk/jdk/net/ExtendedSocketOption/DatagramChannelNAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,6 @@ * @run testng/othervm -Djava.net.preferIPv4Stack=true DatagramChannelNAPITest */ -import jdk.test.lib.net.IPSupport; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -42,7 +41,9 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; +import java.util.Optional; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; @@ -56,7 +57,10 @@ public class DatagramChannelNAPITest { @BeforeTest public void setup() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); try (var dc = DatagramChannel.open()) { if (!dc.supportedOptions().contains(SO_INCOMING_NAPI_ID)) { assertThrows(UOE, () -> dc.getOption(SO_INCOMING_NAPI_ID)); diff --git a/test/jdk/jdk/net/ExtendedSocketOption/DatagramSocketNAPITest.java b/test/jdk/jdk/net/ExtendedSocketOption/DatagramSocketNAPITest.java index 446b74355b6..1ece78adec1 100644 --- a/test/jdk/jdk/net/ExtendedSocketOption/DatagramSocketNAPITest.java +++ b/test/jdk/jdk/net/ExtendedSocketOption/DatagramSocketNAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,12 +37,13 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; +import java.util.Optional; -import jdk.test.lib.net.IPSupport; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertThrows; @@ -56,7 +57,10 @@ public class DatagramSocketNAPITest { @BeforeTest public void setup() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); try (var ds = new DatagramSocket()) { if (!ds.supportedOptions().contains(SO_INCOMING_NAPI_ID)) { assertThrows(UOE, () -> ds.getOption(SO_INCOMING_NAPI_ID)); diff --git a/test/jdk/jdk/net/ExtendedSocketOption/SocketChannelNAPITest.java b/test/jdk/jdk/net/ExtendedSocketOption/SocketChannelNAPITest.java index 898b9725310..b1427d22db6 100644 --- a/test/jdk/jdk/net/ExtendedSocketOption/SocketChannelNAPITest.java +++ b/test/jdk/jdk/net/ExtendedSocketOption/SocketChannelNAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * @run testng/othervm -Djava.net.preferIPv4Stack=true SocketChannelNAPITest */ -import jdk.test.lib.net.IPSupport; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -44,7 +43,9 @@ import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.util.Optional; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; @@ -59,7 +60,10 @@ public class SocketChannelNAPITest { @BeforeTest public void setup() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); try (var s = SocketChannel.open(); var ssc = ServerSocketChannel.open()) { if (!s.supportedOptions().contains(SO_INCOMING_NAPI_ID)) { diff --git a/test/jdk/jdk/net/ExtendedSocketOption/SocketNAPITest.java b/test/jdk/jdk/net/ExtendedSocketOption/SocketNAPITest.java index 2947c6ce878..057bf7cc91d 100644 --- a/test/jdk/jdk/net/ExtendedSocketOption/SocketNAPITest.java +++ b/test/jdk/jdk/net/ExtendedSocketOption/SocketNAPITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,12 +41,13 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; +import java.util.Optional; -import jdk.test.lib.net.IPSupport; import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import static jdk.test.lib.net.IPSupport.diagnoseConfigurationIssue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; @@ -61,7 +62,10 @@ public class SocketNAPITest { @BeforeTest public void setup() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkipException::new).ifPresent(x -> { + throw x; + }); try (var s = new Socket(); var ss = new ServerSocket()) { if (!s.supportedOptions().contains(SO_INCOMING_NAPI_ID)) { diff --git a/test/jdk/jdk/nio/zipfs/ZipFSTester.java b/test/jdk/jdk/nio/zipfs/ZipFSTester.java index 9d70350272f..2ca4686ff14 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSTester.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSTester.java @@ -30,11 +30,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileStore; @@ -56,8 +58,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -73,6 +73,7 @@ import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardCopyOption.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -116,9 +117,9 @@ static void cleanup() throws IOException { } @Test - void test0() throws Exception { + void entryParentTest() throws Exception { List list = new LinkedList<>(); - try (var fs = newZipFileSystem(jarFile, Map.of()); + try (var fs = FileSystems.newFileSystem(jarFile, Map.of()); ZipFile zf = new ZipFile(fs.toString())) { Enumeration zes = zf.entries(); while (zes.hasMoreElements()) { @@ -134,132 +135,199 @@ void test0() throws Exception { } } + // Validates ZipFS provider for expected UOE on directory and non-zip file input @Test - void test1() throws Exception { - // prepare a src for testing - Path src = getTempPath(); - String tmpName = src.toString(); - try (OutputStream os = Files.newOutputStream(src)) { - byte[] bits = new byte[12345]; - RDM.nextBytes(bits); - os.write(bits); - } - - // clone a fs from fs0 and test on it - Path tmpfsPath = getTempPath(); - Map env = new HashMap(); - env.put("create", "true"); - try (var fs = newZipFileSystem(jarFile, Map.of()); - FileSystem copy = newZipFileSystem(tmpfsPath, env)) { - z2zcopy(fs, copy, "/", 0); - - // copy the test jar itself in - Files.copy(Paths.get(fs.toString()), copy.getPath("/foo.jar")); - Path zpath = copy.getPath("/foo.jar"); - try (FileSystem zzfs = FileSystems.newFileSystem(zpath)) { - Files.copy(src, zzfs.getPath("/srcInjarjar")); - } - } - - try (FileSystem fs = newZipFileSystem(tmpfsPath, new HashMap())) { - + void providerValidationUOETest() throws Exception { + var nonZipFile = getNonZipFile(); + var tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { FileSystemProvider provider = fs.provider(); - // newFileSystem(path...) should not throw exception - try (FileSystem fsPath = provider.newFileSystem(tmpfsPath, new HashMap())){} - assertThrows(FileSystemAlreadyExistsException.class, - () -> provider.newFileSystem(new URI("jar", tmpfsPath.toUri().toString(), null), - new HashMap<>()), "newFileSystem(URI...) does not throw exception"); assertThrows(UnsupportedOperationException.class, () -> provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), - new HashMap<>()), "newFileSystem() opens a directory as zipfs"); + Map.of()), "newFileSystem() opens a directory as zipfs"); assertThrows(UnsupportedOperationException.class, - () -> provider.newFileSystem(src, new HashMap()), + () -> provider.newFileSystem(nonZipFile, Map.of()), "newFileSystem() opens a non-zip file as zipfs"); + } finally { + Files.deleteIfExists(tmpfsPath); + Files.deleteIfExists(nonZipFile); + } + } + + // Validates ZipFS provider for expected FSAE on duplicate input + @Test + void providerValidationFSAETest() throws Exception { + var tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { + FileSystemProvider provider = fs.provider(); + assertThrows(FileSystemAlreadyExistsException.class, + () -> provider.newFileSystem(pathToJarURI(tmpfsPath), + Map.of()), "newFileSystem(URI...) does not throw exception"); + } finally { + Files.deleteIfExists(tmpfsPath); + } + } + + // Validates ZipFS provider for newFileSystem(Path, ...) + @Test + void providerValidationTest() throws Exception { + var tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { + FileSystemProvider provider = fs.provider(); + try (FileSystem _ = provider.newFileSystem(tmpfsPath, Map.of())) {} + } finally { + Files.deleteIfExists(tmpfsPath); + } + } + // Performs a sequence of operations where subsequent ops depend on previous state. + // Covers ops such as `copy`, `delete`, `move` + @Test + void entryCopyMoveDeleteFlowTest() throws Exception { + var src = getFile(); + Path tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { // walk walk(fs.getPath("/")); - // copyin - Path dst = getPathWithParents(fs, tmpName); + Path dst = getPathWithParents(fs, src.toString()); Files.copy(src, dst); checkEqual(src, dst); - // copy Path dst2 = getPathWithParents(fs, "/xyz" + RDM.nextInt(100) + - "/efg" + RDM.nextInt(100) + "/foo.class"); + "/efg" + RDM.nextInt(100) + "/foo.class"); Files.copy(dst, dst2); - //dst.moveTo(dst2); + // dst.moveTo(dst2); checkEqual(src, dst2); - // delete Files.delete(dst); assertFalse(Files.exists(dst)); - // moveout - Path dst3 = Paths.get(tmpName + "_Tmp"); + Path dst3 = Paths.get(src + "_Tmp"); Files.move(dst2, dst3); checkEqual(src, dst3); assertFalse(Files.exists(dst2)); - // copyback + move Files.copy(dst3, dst); - Path dst4 = getPathWithParents(fs, tmpName + "_Tmp0"); + Path dst4 = getPathWithParents(fs, src + "_Tmp0"); Files.move(dst, dst4); checkEqual(src, dst4); - // delete Files.delete(dst4); assertFalse(Files.exists(dst4)); Files.delete(dst3); assertFalse(Files.exists(dst3)); + } finally { + Files.deleteIfExists(tmpfsPath); + Files.deleteIfExists(src); + } + } + // Verify existing entry can be renamed within zfs + @Test + void existingEntryMoveTest() throws Exception { + Path tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { // move (existing entry) - Path dst5 = fs.getPath("META-INF/MANIFEST.MF"); - if (Files.exists(dst5)) { - Path dst6 = fs.getPath("META-INF/MANIFEST.MF_TMP"); - Files.move(dst5, dst6); + Path manifest = fs.getPath("META-INF/MANIFEST.MF"); + if (Files.exists(manifest)) { + Path movedManifest = fs.getPath("META-INF/MANIFEST.MF_TMP"); + Files.move(manifest, movedManifest); + // Ensure the move operation was successful + assertFalse(Files.exists(manifest)); + assertTrue(Files.exists(movedManifest)); walk(fs.getPath("/")); + } else { + fail("Unexpected failure, manifest does not exist"); } + } finally { + Files.deleteIfExists(tmpfsPath); + } + } - // newInputStream on dir - Path parent = dst2.getParent(); + // Verify some directory behavior: open dir as stream fails, correct cleanup of empty parent dir + @Test + void directoryOpsTest() throws Exception { + var src = getFile(); + Path tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { + Path pathWithParents = getPathWithParents(fs, "/xyz" + RDM.nextInt(100) + + "/efg" + RDM.nextInt(100) + "/foo.class"); + Files.copy(src, pathWithParents); + // Invoking `newInputStream` on directory should fail + Path parent = pathWithParents.getParent(); assertThrows(FileSystemException.class, () -> Files.newInputStream(parent)); + Files.delete(pathWithParents); + // Clean up directory (when nested child is removed) + assertDoesNotThrow(() -> rmdirs(parent)); + assertFalse(Files.exists(parent)); + } finally { + Files.deleteIfExists(tmpfsPath); + Files.deleteIfExists(src); + } + } - // rmdirs - try { - rmdirs(parent); - } catch (IOException x) { - x.printStackTrace(); - } - + // Verify some channel ops + @Test + void channelTest() throws Exception { + var src = getFile(); + Path tmpfsPath = getTempZipPath(); + createClonedZip(tmpfsPath); + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { + Path dst = getPathWithParents(fs, src.toString()); // newFileChannel() copy in, out and verify via fch fchCopy(src, dst); // in checkEqual(src, dst); - Path tmp = Paths.get(tmpName + "_Tmp"); + Path tmp = Paths.get(src + "_Tmp"); fchCopy(dst, tmp); // out checkEqual(src, tmp); Files.delete(tmp); - // test channels channel(fs, dst); Files.delete(dst); + } finally { + Files.deleteIfExists(tmpfsPath); + Files.deleteIfExists(src); + } + } + // Verify ZipFS can operate on nested zip file entry in another zip + @Test + void nestedZipContentsTest() throws Exception { + // Set up the nested behavior + var src = getFile(); + Path tmpfsPath = getTempZipPath(); + try (var fs = FileSystems.newFileSystem(jarFile, Map.of()); + FileSystem copy = FileSystems.newFileSystem(tmpfsPath, Map.of("create", "true"))) { + z2zcopy(fs, copy, "/", 0); + // copy the test jar itself in + Files.copy(Paths.get(fs.toString()), copy.getPath("/foo.jar")); + Path zpath = copy.getPath("/foo.jar"); + try (FileSystem zzfs = FileSystems.newFileSystem(zpath)) { + Files.copy(src, zzfs.getPath("/srcInjarjar")); + } + } + // Assertions + try (FileSystem fs = FileSystems.newFileSystem(pathToJarURI(tmpfsPath), Map.of())) { // test foo.jar in jar/zipfs #8034802 Path jpath = fs.getPath("/foo.jar"); - System.out.println("walking: " + jpath); try (FileSystem zzfs = FileSystems.newFileSystem(jpath)) { walk(zzfs.getPath("/")); // foojar:/srcInjarjar checkEqual(src, zzfs.getPath("/srcInjarjar")); - - dst = getPathWithParents(zzfs, tmpName); + var dst = getPathWithParents(zzfs, src.toString()); fchCopy(src, dst); checkEqual(src, dst); - tmp = Paths.get(tmpName + "_Tmp"); + Path tmp = Paths.get(src + "_Tmp"); fchCopy(dst, tmp); // out checkEqual(src, tmp); Files.delete(tmp); - channel(zzfs, dst); Files.delete(dst); } @@ -270,100 +338,86 @@ void test1() throws Exception { } @Test - void test2() throws Exception { - Path fs1Path = getTempPath(); - Path fs2Path = getTempPath(); - Path fs3Path = getTempPath(); + void concurrentCopyTest() throws Exception { + Path fs1Path = getTempZipPath(); + Path fs2Path = getTempZipPath(); + Path fs3Path = getTempZipPath(); // create a new filesystem, copy everything from fs - Map env = new HashMap(); - env.put("create", "true"); - FileSystem fs0 = newZipFileSystem(fs1Path, env); + var env = Map.of("create", "true"); + FileSystem fs0 = FileSystems.newFileSystem(fs1Path, env); - final FileSystem fs2 = newZipFileSystem(fs2Path, env); - final FileSystem fs3 = newZipFileSystem(fs3Path, env); + final FileSystem fs2 = FileSystems.newFileSystem(fs2Path, env); + final FileSystem fs3 = FileSystems.newFileSystem(fs3Path, env); System.out.println("copy src: fs -> fs0..."); - try (var fs = newZipFileSystem(jarFile, Map.of())) { + try (var fs = FileSystems.newFileSystem(jarFile, Map.of())) { z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 fs0.close(); // dump to file System.out.println("open fs0 as fs1"); - env = new HashMap(); - final FileSystem fs1 = newZipFileSystem(fs1Path, env); + final FileSystem fs1 = FileSystems.newFileSystem(fs1Path, Map.of()); System.out.println("listing..."); final ArrayList files = new ArrayList<>(); final ArrayList dirs = new ArrayList<>(); list(fs1.getPath("/"), files, dirs); - Thread t0 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 0); - } catch (Exception x) { - x.printStackTrace(); - } + Thread t0 = new Thread(() -> { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 0); + } catch (Exception x) { + x.printStackTrace(); } } - }); - Thread t1 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 1); - } catch (Exception x) { - x.printStackTrace(); - } + Thread t1 = new Thread(() -> { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 1); + } catch (Exception x) { + x.printStackTrace(); } } - }); - Thread t2 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 2); - } catch (Exception x) { - x.printStackTrace(); - } + Thread t2 = new Thread(() -> { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 2); + } catch (Exception x) { + x.printStackTrace(); } } - }); - Thread t3 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(files); - Collections.shuffle(list); - while (!list.isEmpty()) { - Iterator itr = list.iterator(); - while (itr.hasNext()) { - String path = itr.next(); - try { - if (Files.exists(fs2.getPath(path))) { - z2zmove(fs2, fs3, path); - itr.remove(); - } - } catch (FileAlreadyExistsException x) { + Thread t3 = new Thread(() -> { + List list = new ArrayList<>(files); + Collections.shuffle(list); + while (!list.isEmpty()) { + Iterator itr = list.iterator(); + while (itr.hasNext()) { + String path = itr.next(); + try { + if (Files.exists(fs2.getPath(path))) { + z2zmove(fs2, fs3, path); itr.remove(); - } catch (Exception x) { - x.printStackTrace(); } + } catch (FileAlreadyExistsException x) { + itr.remove(); + } catch (Exception x) { + x.printStackTrace(); } } } - }); System.out.println("copying/removing..."); @@ -394,7 +448,7 @@ public void run() { fs3.close(); System.out.println("opening: fs3 as fs4"); - FileSystem fs4 = newZipFileSystem(fs3Path, env); + FileSystem fs4 = FileSystems.newFileSystem(fs3Path, Map.of()); ArrayList files2 = new ArrayList<>(); @@ -503,7 +557,7 @@ private static void checkRead(Path path, byte[] expected) throws IOException { // test entry stream/channel reading @Test void testStreamChannel() throws Exception { - Path zpath = getTempPath(); + Path zpath = getTempZipPath(); try { var crc = new CRC32(); Object[][] entries = getEntries(); @@ -527,7 +581,7 @@ void testStreamChannel() throws Exception { zos.closeEntry(); } } - try (var zfs = newZipFileSystem(zpath, Map.of())) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of())) { for (Object[] e : entries) { Path path = zfs.getPath((String)e[0]); byte[] bytes = (byte[])e[2]; @@ -537,7 +591,7 @@ void testStreamChannel() throws Exception { Files.deleteIfExists(zpath); // [2] create zip via zfs.newByteChannel - try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of("create", "true"))) { for (Object[] e : entries) { // tbd: method is not used try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]), @@ -546,7 +600,7 @@ void testStreamChannel() throws Exception { } } } - try (var zfs = newZipFileSystem(zpath, Map.of())) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -554,12 +608,12 @@ void testStreamChannel() throws Exception { Files.deleteIfExists(zpath); // [3] create zip via Files.write()/newoutputStream/ - try (var zfs = newZipFileSystem(zpath, Map.of("create", "true"))) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of("create", "true"))) { for (Object[] e : entries) { Files.write(zfs.getPath((String)e[0]), (byte[])e[2]); } } - try (var zfs = newZipFileSystem(zpath, Map.of())) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -567,7 +621,7 @@ void testStreamChannel() throws Exception { Files.deleteIfExists(zpath); // [4] create zip via zfs.newByteChannel, with "method_stored" - try (var zfs = newZipFileSystem(zpath, + try (var zfs = FileSystems.newFileSystem(zpath, Map.of("create", true, "noCompression", true))) { for (Object[] e : entries) { try (var sbc = Files.newByteChannel(zfs.getPath((String)e[0]), @@ -576,7 +630,7 @@ void testStreamChannel() throws Exception { } } } - try (var zfs = newZipFileSystem(zpath, Map.of())) { + try (var zfs = FileSystems.newFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -596,7 +650,7 @@ void testTime() throws Exception { .getFileAttributeView(jar, BasicFileAttributeView.class) .readAttributes(); // create a new filesystem, copy this file into it - Path fsPath = getTempPath(); + Path fsPath = getTempZipPath(); try (FileSystem fs = FileSystems.newFileSystem(fsPath, Map.of("create", "true"))) { System.out.println("test copy with timestamps..."); // copyin @@ -630,20 +684,18 @@ void testTime() throws Exception { @Test void test8069211() throws Exception { // create a new filesystem, copy this file into it - Map env = new HashMap(); - env.put("create", "true"); - Path fsPath = getTempPath(); - try (FileSystem fs = newZipFileSystem(fsPath, env);) { + Path fsPath = getTempZipPath(); + try (FileSystem fs = FileSystems.newFileSystem(fsPath, Map.of("create", "true"))) { OutputStream out = Files.newOutputStream(fs.getPath("/foo")); out.write("hello".getBytes()); out.close(); - out.close(); + out.close(); // Intentional second `close` invocation } - try (FileSystem fs = newZipFileSystem(fsPath, new HashMap())) { + try (FileSystem fs = FileSystems.newFileSystem(fsPath, Map.of())) { assertArrayEquals("hello".getBytes(), Files.readAllBytes(fs.getPath("/foo")), - "entry close() failed"); + "Second `close` on entry output stream corrupted /foo"); } catch (Exception x) { - fail("entry close() failed", x); + fail("Second `close` on entry output stream causes unexpected error", x); } finally { Files.delete(fsPath); } @@ -651,26 +703,9 @@ void test8069211() throws Exception { @Test void test8131067() throws Exception { - Map env = new HashMap(); - env.put("create", "true"); - // file name with space character for URI to quote it - File tmp = File.createTempFile("test zipfs", "zip"); - tmp.delete(); // we need a clean path, no file - Path fsPath = tmp.toPath(); - try (FileSystem fs = newZipFileSystem(fsPath, env);) { - Files.write(fs.getPath("/foo"), "hello".getBytes()); - URI fooUri = fs.getPath("/foo").toUri(); - assertArrayEquals("hello".getBytes(), Files.readAllBytes(Paths.get(fooUri)), - "entry close() failed"); - } finally { - Files.delete(fsPath); - } - } - - private static FileSystem newZipFileSystem(Path path, Map env) - throws Exception - { + Path fsPath = Files.createTempFile(Path.of("."), "test zipfs", ".zip"); + Files.delete(fsPath); // we need a clean path, no file // Use URLDecoder (for test only) to remove the double escaped space // character. For the path which is not encoded by UTF-8, we need to // replace "+" by "%2b" manually before feeding into the decoder. @@ -682,16 +717,83 @@ private static FileSystem newZipFileSystem(Path path, Map env) // // Also, we should not use URLEncoder in case of the path has been // encoded. - return FileSystems.newFileSystem( - new URI("jar", URLDecoder.decode(path.toUri().toString() - .replace("+", "%2b"), "utf8"), null), env, null); + try (FileSystem fs = FileSystems.newFileSystem(new URI("jar", + URLDecoder.decode(fsPath.toUri().toString().replace("+", "%2b"), StandardCharsets.UTF_8), + null), Map.of("create", "true"))) { + Files.write(fs.getPath("/foo"), "hello".getBytes()); + URI fooUri = fs.getPath("/foo").toUri(); + assertArrayEquals("hello".getBytes(), Files.readAllBytes(Paths.get(fooUri)), + "entry close() failed"); + } finally { + Files.deleteIfExists(fsPath); + } } - private static Path getTempPath() throws IOException - { - File tmp = File.createTempFile("testzipfs_", "zip"); - tmp.delete(); // we need a clean path, no file - return tmp.toPath(); + /** + * Tests if certain methods throw a NullPointerException if invoked with null + * as specified in java.nio.file.package-info.java + * + * @see 8299864 + */ + @Test + void testFileStoreNullArgs() throws Exception { + try (var fs = FileSystems.newFileSystem(jarFile, Map.of())) { + // file system containing at least one ZipFileStore + FileStore store = fs.getFileStores().iterator().next(); + + // Make sure we are testing the right thing + assertEquals("jdk.nio.zipfs.ZipFileStore", store.getClass().getName()); + + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((String) null)); + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((Class) null)); + assertThrows(NullPointerException.class, () -> store.getAttribute(null)); + assertThrows(NullPointerException.class, () -> store.getFileStoreAttributeView(null)); + } + } + + private static Path getTempNonZipPath() throws IOException { + var path = Files.createTempFile( + Path.of("."), "testzipfs_", ".foo"); + Files.delete(path); + return path; + } + + private static Path getTempZipPath() throws IOException { + var path = Files.createTempFile( + Path.of("."), "testzipfs_", ".zip"); + Files.delete(path); + return path; + } + + private static Path getNonZipFile() throws IOException { + var src = getTempNonZipPath(); + writeRandomBytes(src); + return src; + } + + static Path getFile() throws IOException { + Path src = getTempZipPath(); + writeRandomBytes(src); + return src; + } + + private static void writeRandomBytes(Path src) throws IOException { + try (OutputStream os = Files.newOutputStream(src)) { + byte[] bits = new byte[12345]; + RDM.nextBytes(bits); + os.write(bits); + } + } + + private static void createClonedZip(Path tmpfsPath) throws Exception { + try (var fs = FileSystems.newFileSystem(jarFile, Map.of()); + FileSystem copy = FileSystems.newFileSystem(tmpfsPath, Map.of("create", "true"))) { + z2zcopy(fs, copy, "/", 0); + } + } + + private static URI pathToJarURI(Path path) throws URISyntaxException { + return new URI("jar", path.toUri().toString(), null); } private static void list(Path path, List files, List dirs ) @@ -718,7 +820,7 @@ private static void z2zcopy(FileSystem src, FileSystem dst, String path, if (Files.isDirectory(srcPath)) { if (!Files.exists(dstPath)) { try { - mkdirs(dstPath); + Files.createDirectories(dstPath); } catch (FileAlreadyExistsException x) {} } try (DirectoryStream ds = Files.newDirectoryStream(srcPath)) { @@ -756,7 +858,7 @@ private static void z2zmove(FileSystem src, FileSystem dst, String path) if (Files.isDirectory(srcPath)) { if (!Files.exists(dstPath)) - mkdirs(dstPath); + Files.createDirectories(dstPath); try (DirectoryStream ds = Files.newDirectoryStream(srcPath)) { for (Path child : ds) { z2zmove(src, dst, @@ -767,7 +869,7 @@ private static void z2zmove(FileSystem src, FileSystem dst, String path) //System.out.println("moving..." + path); Path parent = dstPath.getParent(); if (parent != null && Files.notExists(parent)) - mkdirs(parent); + Files.createDirectories(parent); Files.move(srcPath, dstPath); } } @@ -814,18 +916,6 @@ public FileVisitResult postVisitDirectory(Path dir, }); } - private static void mkdirs(Path path) throws IOException { - if (Files.exists(path)) - return; - path = path.toAbsolutePath(); - Path parent = path.getParent(); - if (parent != null) { - if (Files.notExists(parent)) - mkdirs(parent); - } - Files.createDirectory(path); - } - private static void rmdirs(Path path) throws IOException { while (path != null && path.getNameCount() != 0) { Files.delete(path); @@ -915,11 +1005,8 @@ private static void checkEqual(Path src, Path dst) throws IOException private static void fchCopy(Path src, Path dst) throws IOException { - Set read = new HashSet<>(); - read.add(READ); - Set openwrite = new HashSet<>(); - openwrite.add(CREATE_NEW); - openwrite.add(WRITE); + Set read = Set.of(READ); + Set openwrite = Set.of(CREATE_NEW, WRITE); try (FileChannel srcFc = src.getFileSystem() .provider() @@ -939,11 +1026,8 @@ private static void fchCopy(Path src, Path dst) throws IOException private static void chCopy(Path src, Path dst) throws IOException { - Set read = new HashSet<>(); - read.add(READ); - Set openwrite = new HashSet<>(); - openwrite.add(CREATE_NEW); - openwrite.add(WRITE); + Set read = Set.of(READ); + Set openwrite = Set.of(CREATE_NEW, WRITE); try (SeekableByteChannel srcCh = Files.newByteChannel(src, read); SeekableByteChannel dstCh = Files.newByteChannel(dst, openwrite)) @@ -983,8 +1067,7 @@ static void channel(FileSystem fs, Path path) throws Exception { System.out.println("test ByteChannel..."); - Set read = new HashSet<>(); - read.add(READ); + Set read = Set.of(READ); int n = 0; ByteBuffer bb = null; ByteBuffer bb2 = null; @@ -1027,29 +1110,7 @@ static Path getPathWithParents(FileSystem fs, String name) Path path = fs.getPath(name); Path parent = path.getParent(); if (parent != null && Files.notExists(parent)) - mkdirs(parent); + Files.createDirectories(parent); return path; } - - /** - * Tests if certain methods throw a NullPointerException if invoked with null - * as specified in java.nio.file.package-info.java - * - * @see 8299864 - */ - @Test - void testFileStoreNullArgs() throws Exception { - try (var fs = newZipFileSystem(jarFile, Map.of())) { - // file system containing at least one ZipFileStore - FileStore store = fs.getFileStores().iterator().next(); - - // Make sure we are testing the right thing - assertEquals("jdk.nio.zipfs.ZipFileStore", store.getClass().getName()); - - assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((String) null)); - assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((Class) null)); - assertThrows(NullPointerException.class, () -> store.getAttribute(null)); - assertThrows(NullPointerException.class, () -> store.getFileStoreAttributeView(null)); - } - } } diff --git a/test/jdk/sun/security/rsa/GenKeyStore.java b/test/jdk/sun/security/rsa/GenKeyStore.java index 26178e9a576..8b0016e77af 100644 --- a/test/jdk/sun/security/rsa/GenKeyStore.java +++ b/test/jdk/sun/security/rsa/GenKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,9 @@ // program to generate rsakeys.ks. does not need to run during testing // checked into the workspace so that the keystore file can be recreated // in the future if needed. +// +// To run: +// java --add-exports java.base/sun.security.x509=ALL-UNNAMED GenKeyStore.java // @author Andreas Sterbenz @@ -43,26 +46,22 @@ public class GenKeyStore { static final char[] password = "test12".toCharArray(); - private static X509Certificate getCertificate(String suffix, PublicKey publicKey, PrivateKey privateKey) throws Exception { + private static X509Certificate getCertificate(String suffix, + PublicKey publicKey, + PrivateKey privateKey) throws Exception { X500Name name = new X500Name("CN=Dummy Certificate " + suffix); String algorithm = "SHA1with" + publicKey.getAlgorithm(); Date date = new Date(); - AlgorithmId algID = AlgorithmId.getAlgorithmId(algorithm); X509CertInfo certInfo = new X509CertInfo(); - - certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V1)); - certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(1)); - certInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algID)); - certInfo.set(X509CertInfo.SUBJECT, name); - certInfo.set(X509CertInfo.ISSUER, name); - certInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); - certInfo.set(X509CertInfo.VALIDITY, new CertificateValidity(date, date)); - - X509CertImpl cert = new X509CertImpl(certInfo); - cert.sign(privateKey, algorithm); - - return cert; + certInfo.setVersion(new CertificateVersion(CertificateVersion.V1)); + certInfo.setSerialNumber(new CertificateSerialNumber(1)); + certInfo.setSubject(name); + certInfo.setIssuer(name); + certInfo.setKey(new CertificateX509Key(publicKey)); + certInfo.setValidity(new CertificateValidity(date, date)); + + return X509CertImpl.newSigned(certInfo, privateKey, algorithm); } private static void addToKeyStore(KeyStore ks, KeyPair kp, String name) throws Exception { diff --git a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java index 6d21cd5613a..4d9b7500d87 100644 --- a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java +++ b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java @@ -24,7 +24,7 @@ /* * @test * @bug 8181157 8202537 8234347 8236548 8261279 8322647 8174269 8346948 - * 8354548 + * 8354548 8381379 * @modules jdk.localedata * @summary Checks CLDR time zone names are generated correctly at * either build or runtime @@ -32,15 +32,21 @@ */ import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.time.format.TextStyle; import java.util.Arrays; +import java.util.Date; import java.util.Locale; import java.util.Objects; import java.util.TimeZone; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -217,6 +223,18 @@ private static Object[][] sampleTZs() { }; } + private static Stream explicitDstOffsets() { + return Stream.of( + Arguments.of(ZonedDateTime.of(2026, 4, 5, 0, 0, 0, 0, ZoneId.of("Europe/Dublin")), "Irish Standard Time"), + Arguments.of(ZonedDateTime.of(2026, 12, 5, 0, 0, 0, 0, ZoneId.of("Europe/Dublin")), "Greenwich Mean Time"), + Arguments.of(ZonedDateTime.of(2026, 4, 5, 0, 0, 0, 0, ZoneId.of("Eire")), "Irish Standard Time"), + Arguments.of(ZonedDateTime.of(2026, 12, 5, 0, 0, 0, 0, ZoneId.of("Eire")), "Greenwich Mean Time"), + Arguments.of(ZonedDateTime.of(2026, 4, 5, 0, 0, 0, 0, ZoneId.of("America/Vancouver")), "Pacific Daylight Time"), + // This needs to change once TZDB adopts -7 offset year round, and CLDR uses explicit dst offset + // namely, "Pacific Standard Time" -> "Pacific Daylight Time" + Arguments.of(ZonedDateTime.of(2026, 12, 5, 0, 0, 0, 0, ZoneId.of("America/Vancouver")), "Pacific Standard Time") + ); + } @ParameterizedTest @MethodSource("sampleTZs") @@ -248,4 +266,19 @@ public void test_getZoneStrings() { .anyMatch(name -> Objects.isNull(name) || name.isEmpty()), "getZoneStrings() returned array containing non-empty string element(s)"); } + + // Explicit metazone dst offset test. As of CLDR v48, only Europe/Dublin utilizes + // this attribute, but will be used for America/Vancouver once CLDR adopts the + // explicit offset for that zone, which warrants the test data modification. + @ParameterizedTest + @MethodSource("explicitDstOffsets") + public void test_ExplicitMetazoneOffsets(ZonedDateTime zdt, String expected) { + // java.time + assertEquals(expected, DateTimeFormatter.ofPattern("zzzz").format(zdt)); + + // java.text/util + var sdf = new SimpleDateFormat("zzzz"); + sdf.setTimeZone(TimeZone.getTimeZone(zdt.getZone())); + assertEquals(expected, sdf.format(Date.from(zdt.toInstant()))); + } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java new file mode 100644 index 00000000000..5ae6b17a24c --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnTypes.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8374020 + * @summary Verify types are set back to the AST. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @compile TypeAnnotationsOnTypes.java + * @run main TypeAnnotationsOnTypes + */ + +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.UnionType; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class TypeAnnotationsOnTypes { + + public static void main(String... args) throws Exception { + new TypeAnnotationsOnTypes().run(); + } + + ToolBox tb = new ToolBox(); + + void run() throws Exception { + typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); + } + + void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.util.List; + import java.lang.annotation.Target; + + class Test { + + void f() { + @TA List l1; + @TA String[] l2; + @TA TypeVar l3; + try { + } catch (@TA IllegalStateException | NullPointerException | @TA IllegalArgumentException ex) {} + } + + @Target(ElementType.TYPE_USE) + @interface TA {} + } + """); + Files.createDirectories(classes); + List actual = new ArrayList<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + @Override + public Void visitVariable(VariableTree node, Void p) { + TreePath typePath = + new TreePath(getCurrentPath(), node.getType()); + actual.add(node.getName() + + ": type on variable: " + + typeToString(trees.getTypeMirror(getCurrentPath())) + + ": type on type: " + + typeToString(trees.getTypeMirror(typePath))); + return super.visitVariable(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run() + .writeAll(); + + List expected = List.of( + "l1: type on variable: java.util.@Test.TA List: type on type: java.util.@Test.TA List", + "l2: type on variable: java.lang.@Test.TA String[]: type on type: java.lang.@Test.TA String[]", + "l3: type on variable: @Test.TA TypeVar: type on type: @Test.TA TypeVar", + "ex: type on variable: java.lang.@Test.TA IllegalStateException | java.lang.NullPointerException | java.lang.@Test.TA IllegalArgumentException: " + + "type on type: java.lang.@Test.TA IllegalStateException | java.lang.NullPointerException | java.lang.@Test.TA IllegalArgumentException" + ); + + actual.forEach(System.out::println); + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + } + + static String typeToString(TypeMirror type) { + if (type != null && type.getKind() == TypeKind.UNION) { + return ((UnionType) type).getAlternatives().stream().map(t -> typeToString(t)).collect(Collectors.joining(" | ")); + } else { + return String.valueOf(type); + } + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java index 2e68e18f8f7..e5a1e5650d3 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java @@ -23,15 +23,16 @@ /* * @test - * @bug 8371155 + * @bug 8371155 8379550 * @summary Verify type annotations on local-like variables are propagated to * their types at an appropriate time. * @library /tools/lib * @modules * jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main + * jdk.jdeps/com.sun.tools.javap * @build toolbox.ToolBox toolbox.JavacTask - * @run main TypeAnnotationsOnVariables + * @run junit TypeAnnotationsOnVariables */ import com.sun.source.tree.LambdaExpressionTree; @@ -41,31 +42,44 @@ import com.sun.source.util.TaskListener; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; +import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; +import java.lang.classfile.constantpool.ConstantPool; +import java.lang.classfile.constantpool.Utf8Entry; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.UnionType; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; import toolbox.JavacTask; +import toolbox.JavapTask; +import toolbox.Task; import toolbox.ToolBox; public class TypeAnnotationsOnVariables { - public static void main(String... args) throws Exception { - new TypeAnnotationsOnVariables().run(); - } - - ToolBox tb = new ToolBox(); - - void run() throws Exception { - typeAnnotationInConstantExpressionFieldInit(Paths.get(".")); - } + private static final Pattern CP_REFERENCE = Pattern.compile("#([1-9][0-9]*)"); + final ToolBox tb = new ToolBox(); + Path base; - void typeAnnotationInConstantExpressionFieldInit(Path base) throws Exception { + @Test + void typeAnnotationInConstantExpressionFieldInit() throws Exception { Path src = base.resolve("src"); Path classes = base.resolve("classes"); tb.writeJavaFiles(src, @@ -203,9 +217,208 @@ static String typeToString(TypeMirror type) { } static String treeToString(Tree tree) { - if (tree.toString().contains("\n")) { - System.err.println("!!!"); - } return String.valueOf(tree).replaceAll("\\R", " "); } + + @Test + void properPathForLocalVarsInLambdas() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.function.Supplier; + + class Test { + @Target(ElementType.TYPE_USE) + @interface TypeAnno { } + + void o() { + Runnable r = () -> { + @TypeAnno long test1 = 0; + while (true) { + @TypeAnno long test2 = 0; + System.err.println(test2); + try (@TypeAnno AutoCloseable ac = null) { + System.err.println(ac); + } catch (@TypeAnno Exception e1) { + System.err.println(e1); + } + try { + "".length(); + } catch (@TypeAnno final Exception e2) { + System.err.println(e2); + } + try { + "".length(); + } catch (@TypeAnno IllegalStateException | @TypeAnno NullPointerException | IllegalArgumentException e3) { + System.err.println(e3); + } + Runnable r2 = () -> { + @TypeAnno long test3 = 0; + while (true) { + @TypeAnno long test4 = 0; + System.err.println(test4); + } + }; + Object o = null; + if (o instanceof @TypeAnno String s) { + System.err.println(s); + } + } + }; + } + void lambdaInClass() { + class C { + Runnable r = () -> { + @TypeAnno long test1 = 0; + System.err.println(test1); + }; + } + } + void classInLambda() { + Runnable r = () -> { + class C { + void method() { + @TypeAnno long test1 = 0; + System.err.println(test1); + } + } + }; + } + } + """); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .run() + .writeAll(); + + Path testClass = classes.resolve("Test.class"); + TestClassDesc testClassDesc = TestClassDesc.create(testClass); + MethodModel oMethod = singletonValue(testClassDesc.name2Method().get("o")); + var oTypeAnnos = getAnnotations(oMethod); + assertFalse(oTypeAnnos.isPresent(), () -> oTypeAnnos.toString()); + + checkTypeAnnotations(testClassDesc, + "lambda$o$0", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=151, index=0}", + " Test$TypeAnno", + " 1: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=4, length=146, index=2}", + " Test$TypeAnno", + " 2: LTest$TypeAnno;(): RESOURCE_VARIABLE, {start_pc=14, length=52, index=4}", + " Test$TypeAnno", + " 3: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=2", + " Test$TypeAnno", + " 4: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=3", + " Test$TypeAnno", + " 5: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=4", + " Test$TypeAnno", + " 6: LTest$TypeAnno;(): EXCEPTION_PARAMETER, exception_index=5", + " Test$TypeAnno", + " 7: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=142, length=8, index=6}", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$o$1", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=12, index=0}", + " Test$TypeAnno", + " 1: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=4, length=7, index=2}", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$classInLambda$0"); + + checkTypeAnnotations(TestClassDesc.create(classes.resolve("Test$1C.class")), + "lambda$new$0", + " 0: LTest$TypeAnno;(): LOCAL_VARIABLE, {start_pc=2, length=8, index=0}", + " Test$TypeAnno"); + } + + private void checkTypeAnnotations(TestClassDesc testClassDesc, + String lambdaMethodName, + String... expectedEntries) throws IOException { + MethodModel lambdaMethod = singletonValue(testClassDesc.name2Method().get(lambdaMethodName)); + var lambdaTypeAnnos = getAnnotations(lambdaMethod); + if (expectedEntries.length == 0) { + assertFalse(lambdaTypeAnnos.isPresent(), () -> lambdaTypeAnnos.toString()); + } else { + assertTrue(lambdaTypeAnnos.isPresent(), () -> lambdaTypeAnnos.toString()); + assertEquals(expectedEntries.length / 2, + lambdaTypeAnnos.orElseThrow().annotations().size(), + () -> lambdaTypeAnnos.orElseThrow().annotations().toString()); + + checkJavapOutput(testClassDesc, + List.of(expectedEntries)); + } + } + + private T singletonValue(List values) { + assertEquals(1, values.size()); + return values.get(0); + } + + private Optional getAnnotations(MethodModel m) { + return m.findAttribute(Attributes.code()) + .orElseThrow() + .findAttribute(Attributes.runtimeInvisibleTypeAnnotations()); + } + + void checkJavapOutput(TestClassDesc testClassDesc, List expectedOutput) throws IOException { + String javapOut = new JavapTask(tb) + .options("-v", "-p") + .classes(testClassDesc.pathToClass().toString()) + .run() + .getOutput(Task.OutputKind.DIRECT); + + StringBuilder expandedJavapOutBuilder = new StringBuilder(); + Matcher m = CP_REFERENCE.matcher(javapOut); + + while (m.find()) { + String cpIndexText = m.group(1); + int cpIndex = Integer.parseInt(cpIndexText); + m.appendReplacement(expandedJavapOutBuilder, Matcher.quoteReplacement(testClassDesc.cpIndex2Name().getOrDefault(cpIndex, cpIndexText))); + } + + m.appendTail(expandedJavapOutBuilder); + + String expandedJavapOut = expandedJavapOutBuilder.toString(); + + for (String expected : expectedOutput) { + if (!expandedJavapOut.contains(expected)) { + System.err.println(expandedJavapOut); + throw new AssertionError("unexpected output"); + } + } + } + + record TestClassDesc(Path pathToClass, + Map> name2Method, + Map cpIndex2Name) { + public static TestClassDesc create(Path pathToClass) throws IOException{ + ClassModel model = ClassFile.of().parse(pathToClass); + Map> name2Method = + model.methods() + .stream() + .collect(Collectors.groupingBy(m -> m.methodName().stringValue())); + ConstantPool cp = model.constantPool(); + int cpSize = cp.size(); + Map cpIndex2Name = new HashMap<>(); + + for (int i = 1; i < cpSize; i++) { + if (cp.entryByIndex(i) instanceof Utf8Entry string) { + cpIndex2Name.put(i, string.stringValue()); + } + } + + return new TestClassDesc(pathToClass, name2Method, cpIndex2Name); + } + } + + @BeforeEach + void setUp(TestInfo thisTest) { + base = Path.of(thisTest.getTestMethod().orElseThrow().getName()); + } } diff --git a/test/lib/jdk/test/lib/jfr/Events.java b/test/lib/jdk/test/lib/jfr/Events.java index 8bbf22ca63a..03e1e39cfe3 100644 --- a/test/lib/jdk/test/lib/jfr/Events.java +++ b/test/lib/jdk/test/lib/jfr/Events.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.nio.file.Path; import java.time.Duration; import java.time.Instant; +import java.util.Comparator; import java.util.List; import jdk.jfr.AnnotationElement; @@ -280,6 +281,20 @@ public static List fromRecording(Recording recording) throws IOEx return RecordingFile.readAllEvents(makeCopy(recording)); } + /** + * Creates a list of events from a recording, ordered by the end time. + * + * @param recording recording, not {@code null} + * @return a list, not null + * @throws IOException if an event set could not be created due to I/O + * errors. + */ + public static List fromRecordingOrdered(Recording recording) throws IOException { + List events = fromRecording(recording); + events.sort(Comparator.comparing(RecordedEvent::getEndTime)); + return events; + } + public static RecordingFile copyTo(Recording r) throws IOException { return new RecordingFile(makeCopy(r)); } diff --git a/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java b/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java new file mode 100644 index 00000000000..53fa2378ead --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java @@ -0,0 +1,206 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package org.openjdk.bench.vm.gc; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import org.openjdk.bench.util.InMemoryJavaCompiler; + +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.code.NMethod; + +/* + * Nmethods have OOPs and GC barriers emmedded into their code. + * GCs patch them which causes invalidation of nmethods' code. + * + * This benchmark can be used to estimate the cost of patching + * OOPs and GC barriers. + * + * We create 5000 nmethods which access fields of a class. + * We measure the time of different GC cycles to see + * the impact of patching nmethods. + * + * The benchmark parameters are method count and accessed field count. + */ + +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(value = 1, jvmArgsAppend = { + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:lib-test/wb.jar", + "-XX:-UseCodeCacheFlushing" +}) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +public class GCPatchingNmethodCost { + + private static final int COMP_LEVEL = 1; + private static final String FIELD_USER = "FieldUser"; + + public static Fields fields; + + private static TestMethod[] methods = {}; + private static byte[] BYTE_CODE; + private static WhiteBox WB; + + @Param({"5000"}) + public int methodCount; + + @Param({"0", "2", "4", "8"}) + public int accessedFieldCount; + + public static class Fields { + public String f1; + public String f2; + public String f3; + public String f4; + public String f5; + public String f6; + public String f7; + public String f8; + public String f9; + } + + private static final class TestMethod { + private final Method method; + + public TestMethod(Method method) throws Exception { + this.method = method; + WB.testSetDontInlineMethod(method, true); + } + + public void profile() throws Exception { + method.invoke(null); + WB.markMethodProfiled(method); + } + + public void invoke() throws Exception { + method.invoke(null); + } + + public void compile() throws Exception { + WB.enqueueMethodForCompilation(method, COMP_LEVEL); + while (WB.isMethodQueuedForCompilation(method)) { + Thread.onSpinWait(); + } + if (WB.getMethodCompilationLevel(method) != COMP_LEVEL) { + throw new IllegalStateException("Method " + method + " is not compiled at the compilation level: " + COMP_LEVEL + ". Got: " + WB.getMethodCompilationLevel(method)); + } + } + + public NMethod getNMethod() { + return NMethod.get(method, false); + } + } + + private static ClassLoader createClassLoader() { + return new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (!name.equals(FIELD_USER)) { + return super.loadClass(name); + } + + return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length); + } + }; + } + + private static void createTestMethods(int accessedFieldCount, int count) throws Exception { + String javaCode = "public class " + FIELD_USER + " {"; + String field = GCPatchingNmethodCost.class.getName() + ".fields.f"; + javaCode += "public static void accessFields() {"; + for (int i = 1; i <= accessedFieldCount; i++) { + javaCode += field + i + "= " + field + i + " + " + i + ";"; + } + javaCode += "}}"; + + BYTE_CODE = InMemoryJavaCompiler.compile(FIELD_USER, javaCode); + + fields = new Fields(); + + methods = new TestMethod[count]; + for (int i = 0; i < count; i++) { + var cl = createClassLoader().loadClass(FIELD_USER); + Method method = cl.getMethod("accessFields"); + methods[i] = new TestMethod(method); + methods[i].profile(); + methods[i].compile(); + } + } + + private static void initWhiteBox() { + WB = WhiteBox.getWhiteBox(); + } + + @Setup(Level.Trial) + public void setupCodeCache() throws Exception { + initWhiteBox(); + createTestMethods(accessedFieldCount, methodCount); + System.gc(); + } + + @Setup(Level.Iteration) + public void setupIteration() { + fields = new Fields(); + } + + @Benchmark + public void youngGC() throws Exception { + fields = null; + WB.youngGC(); + } + + @Benchmark + public void fullGC() throws Exception { + fields = null; + WB.fullGC(); + } + + @Benchmark + public void systemGC() throws Exception { + fields = null; + System.gc(); + } +}