diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 1013f38baa77..051be9021d7c 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -666,7 +666,7 @@ define SetupRunMicroTestBody $$($1_MICRO_ITER) $$($1_MICRO_FORK) $$($1_MICRO_TIME) \ $$($1_MICRO_WARMUP_ITER) $$($1_MICRO_WARMUP_TIME) \ $$($1_MICRO_VM_OPTIONS) $$($1_MICRO_BASIC_OPTIONS) $$(MICRO_OPTIONS) \ - $$($1_TEST_NAME) \ + $$(call ShellQuote,$$($1_TEST_NAME)) \ > >($(TEE) $$($1_TEST_RESULTS_DIR)/micro.txt) \ && $$(ECHO) $$$$? > $$($1_EXITCODE) \ || $$(ECHO) $$$$? > $$($1_EXITCODE) \ @@ -1293,7 +1293,7 @@ UseSpecialTestHandler = \ # Now process each test to run and setup a proper make rule $(foreach test, $(TESTS_TO_RUN), \ - $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ + $(eval TEST_ID := $(shell $(ECHO) $(call ShellQuote,$(strip $(test))) | \ $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(eval ALL_TEST_IDS += $(TEST_ID)) \ $(if $(call UseCustomTestHandler, $(test)), \ @@ -1373,10 +1373,10 @@ run-test-report: post-run-test $(PRINTF) >> $(TEST_SUMMARY) "%2s %-49s %5s %5s %5s %5s %5s %2s\n" " " \ TEST TOTAL PASS FAIL ERROR SKIP " " $(foreach test, $(TESTS_TO_RUN), \ - $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ + $(eval TEST_ID := $(shell $(ECHO) $(call ShellQuote,$(strip $(test))) | \ $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(ECHO) >> $(TEST_LAST_IDS) $(TEST_ID) $(NEWLINE) \ - $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '_')) \ + $(eval NAME_PATTERN := $(shell $(ECHO) $(call ShellQuote,$(test)) | $(TR) -c '\n' '_')) \ $(if $(filter __________________________________________________%, $(NAME_PATTERN)), \ $(eval TEST_NAME := ) \ $(PRINTF) >> $(TEST_SUMMARY) "%2s %-49s\n" " " "$(test)" $(NEWLINE) \ diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index 50b632a49f91..fd1591897c54 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -211,6 +211,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \ DISABLED_WARNINGS_gcc_safepointMechanism.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_shenandoahGenerationalHeap.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_shenandoahHeap.cpp := stringop-overflow, \ + DISABLED_WARNINGS_gcc_shenandoahInPlacePromoter.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_shenandoahLock.cpp := stringop-overflow, \ DISABLED_WARNINGS_gcc_stubGenerator_s390.cpp := unused-const-variable, \ DISABLED_WARNINGS_gcc_synchronizer.cpp := stringop-overflow, \ diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 41da22f8cb2f..095c2eb163b0 100644 --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -43,10 +43,9 @@ include lib/CoreLibraries.gmk $(eval $(call SetupJdkLibrary, BUILD_LIBNET, \ NAME := net, \ OPTIMIZATION := LOW, \ - DISABLED_WARNINGS_gcc_net_util_md.c := format-nonliteral unused-variable, \ + DISABLED_WARNINGS_gcc_net_util_md.c := format-nonliteral, \ DISABLED_WARNINGS_gcc_NetworkInterface.c := unused-function, \ - DISABLED_WARNINGS_clang_net_util_md.c := format-nonliteral \ - unused-variable, \ + DISABLED_WARNINGS_clang_net_util_md.c := format-nonliteral, \ DISABLED_WARNINGS_clang_NetworkInterface.c := unused-function, \ DISABLED_WARNINGS_clang_aix_DefaultProxySelector.c := \ deprecated-non-prototype, \ @@ -78,8 +77,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBNIO, \ libnio/ch \ libnio/fs \ libjvm, \ - DISABLED_WARNINGS_clang_Net.c := unused-function unused-variable, \ - DISABLED_WARNINGS_clang_UnixNativeDispatcher.c := unused-variable, \ + DISABLED_WARNINGS_clang_Net.c := unused-function, \ JDK_LIBS := libjava libnet, \ LIBS_linux := $(LIBDL) $(LIBPTHREAD), \ LIBS_aix := $(LIBDL), \ diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index 6774e708f99a..4700867c71ab 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -75,6 +75,7 @@ else # java.lang.foreign tests BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncInvokers := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libDetachAfterExit := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerUnnamed := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerModule := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLoaderLookupInvoker := -pthread diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 89935680605e..65bba11347b2 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1810,10 +1810,6 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const { __ bind(*_verified_entry); } - if (VerifyStackAtCalls) { - Unimplemented(); - } - C->output()->set_frame_complete(__ offset()); if (C->has_mach_constant_base_node()) { @@ -3679,10 +3675,6 @@ encode %{ %} enc_class aarch64_enc_call_epilog() %{ - if (VerifyStackAtCalls) { - // Check that stack depth is unchanged: find majik cookie on stack - __ call_Unimplemented(); - } if (tf()->returns_value_type_as_fields() && !_method->is_method_handle_intrinsic() && _method->return_type()->is_loaded()) { // The last return value is not set by the callee but used to pass the null marker to compiled code. // Search for the corresponding projection, get the register and emit code that initializes it. diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 2ae9f0d6e9fe..ea55b58f8100 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -477,8 +477,8 @@ class Address { // Verify the value is trivially destructible regardless of mode, so our // destructor can also be trivial, and so our assignment operator doesn't // need to destruct the old value before copying over it. - static_assert(std::is_trivially_destructible::value, "must be"); - static_assert(std::is_trivially_destructible::value, "must be"); + static_assert(std::is_trivially_destructible::value); + static_assert(std::is_trivially_destructible::value); Address& operator=(const Address& a) { _mode = a._mode; diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp index add96b787083..facde35c0287 100644 --- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp @@ -778,7 +778,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { case StubId::c1_new_null_free_array_id: __ cmpw(t0, Klass::_lh_array_tag_flat_value); // the array can be a flat array. __ br(Assembler::EQ, ok); - __ cmpw(t0, Klass::_lh_array_tag_ref_value); // the array cannot be a flat array (due to the InlineArrayElementMaxFlatSize, etc.) + __ cmpw(t0, Klass::_lh_array_tag_ref_value); // the array cannot be a flat array. __ br(Assembler::EQ, ok); __ stop("assert(is an object or value type array klass)"); break; diff --git a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp index 739d0ee95213..a6036287efb4 100644 --- a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp @@ -63,12 +63,13 @@ inline frame FreezeBase::sender(const frame& f) { frame::CompiledFramePointers cfp = f.compiled_frame_details(); int slot = 0; - CodeBlob* sender_cb = CodeCache::find_blob_and_oopmap(*cfp.sender_pc_addr, slot); + address sender_pc = ContinuationHelper::return_address_at((intptr_t*)cfp.sender_pc_addr); + CodeBlob* sender_cb = CodeCache::find_blob_and_oopmap(sender_pc, slot); return sender_cb != nullptr - ? frame(cfp.sender_sp, cfp.sender_sp, *cfp.saved_fp_addr, *cfp.sender_pc_addr, sender_cb, - slot == -1 ? nullptr : sender_cb->oop_map_for_slot(slot, *cfp.sender_pc_addr), false) - : frame(cfp.sender_sp, cfp.sender_sp, *cfp.saved_fp_addr, *cfp.sender_pc_addr); + ? frame(cfp.sender_sp, cfp.sender_sp, *cfp.saved_fp_addr, sender_pc, sender_cb, + slot == -1 ? nullptr : sender_cb->oop_map_for_slot(slot, sender_pc), false) + : frame(cfp.sender_sp, cfp.sender_sp, *cfp.saved_fp_addr, sender_pc); } template diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp index 283a0a1e3c27..7e44c4152d5c 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp @@ -632,7 +632,9 @@ void frame::describe_pd(FrameValues& values, int frame_no) { ret_pc_loc = (intptr_t*)cfp.sender_pc_addr; fp_loc = (intptr_t*)cfp.saved_fp_addr; } - address ret_pc = *(address*)ret_pc_loc; + // Strip without authenticating: describe may see unsigned or broken LRs, + // and is_return_barrier_entry() compares against a raw stub address. + address ret_pc = pauth_strip_pointer(*(address*)ret_pc_loc); values.describe(frame_no, ret_pc_loc, Continuation::is_return_barrier_entry(ret_pc) ? "return address (return barrier)" : "return address"); values.describe(-1, fp_loc, "saved fp", 0); // "unowned" as value belongs to sender diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 6dcb27ee578a..668176d86f82 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -258,7 +258,7 @@ void BarrierSetAssembler::copy_store_at(MacroAssembler* masm, void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath) { // If mask changes we need to ensure that the inverse is still encodable as an immediate - STATIC_ASSERT(JNIHandles::tag_mask == 0b11); + static_assert(JNIHandles::tag_mask == 0b11); __ andr(obj, obj, ~JNIHandles::tag_mask); __ ldr(obj, Address(obj, 0)); // *obj } diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 9037f6863646..e3af4f150f76 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -31,10 +31,13 @@ #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" +#include "nativeInst_aarch64.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #ifdef COMPILER1 @@ -493,6 +496,47 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #undef __ +address ShenandoahBarrierSetAssembler::parse_jump_address(address pc) { + NativeInstruction* ni = nativeInstruction_at(pc); + assert(ni->is_jump(), "Initial code version: GC barrier fastpath must be a jump"); + NativeJump* jmp = nativeJump_at(pc); + return jmp->jump_destination(); +} + +static uint32_t encode_patchable_nop() { + return 0xD503201F; +} + +static uint32_t encode_patchable_jump(address pc, address target_pc) { + int32_t disp = checked_cast((intptr_t)target_pc - (intptr_t)pc); + int64_t imm26 = disp >> 2; + guarantee(Assembler::is_simm(imm26, 26), "Maximum offset is 128MiB"); + return 0x14000000 | (imm26 & 0x03FFFFFF); +} + +void ShenandoahBarrierSetAssembler::insert_patchable_nop(address pc) { + *((uint32_t*)pc) = encode_patchable_nop(); + assert(nativeInstruction_at(pc)->is_nop(), "Sanity"); + if (!UseSingleICacheInvalidation) { + ICache::invalidate_word(pc); + } +} + +void ShenandoahBarrierSetAssembler::insert_patchable_jump(address pc, address target_pc) { + *((uint32_t*)pc) = encode_patchable_jump(pc, target_pc); + if (!UseSingleICacheInvalidation) { + ICache::invalidate_word(pc); + } +} + +bool ShenandoahBarrierSetAssembler::is_patchable_nop(address pc) { + return *((uint32_t*)pc) == encode_patchable_nop(); +} + +bool ShenandoahBarrierSetAssembler::is_patchable_jump(address pc, address target_pc) { + return *((uint32_t*)pc) == encode_patchable_jump(pc, target_pc); +} + #ifdef COMPILER1 #define __ ce->masm()-> @@ -707,27 +751,70 @@ void ShenandoahBarrierStubC2::cardtable(MacroAssembler& masm, Address address, R } } -void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) { - Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); +void ShenandoahBarrierStubC2::patchable_jump(MacroAssembler& masm, const char gc_state, bool jump_when_state, Register tmp1, Register tmp2, Label* L_target) { + const size_t check_size = 8; + PhaseOutput* const output = Compile::current()->output(); - Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state))); + if (output->in_scratch_emit_size()) { + // Avoid binding L_target and emitting more branches in scratch emits. + // We know the patched check is exactly 1 instruction long in release, + // and verification adds more instructions. + const int nops = 1 DEBUG_ONLY(+ check_size); + for (size_t c = 0; c < nops; c++) { + __ nop(); + } + return; + } - // We piggyback on scratch_emit_size mode to compute the slowpath stub size. - // We'll use that information to decide whether we need a far jump to the - // stub entry point or not. In scratch_emit_size mode we don't bind entry() - // because otherwise it will be rebound when we later emit the instructions - // for real. - if (_needs_far_jump) { - __ ldrb(tmp, gc_state_fast); - __ cbz(tmp, *continuation()); - __ b(output->in_scratch_emit_size() ? *continuation() : *entry()); +#ifdef ASSERT + Label L_fake_entry, L_skip; + Address gc_state_addr(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); + + address check_start = __ pc(); + + __ ldrb(tmp1, gc_state_addr); + __ mov(tmp2, gc_state); + __ andr(tmp2, tmp1, tmp2); + + // Emit the secondary jump and use it to cross-check against the actual GC state. + // This also checks that all interesting GC state transitions are done non-racily + // from the perspective of the thread executing the nmethod. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ b(L_fake_entry); + + // Currently hot-patched to NOP. + if (jump_when_state) { + __ cbz(tmp2, L_skip); } else { - __ ldrb(tmp, gc_state_fast); - __ cbnz(tmp, output->in_scratch_emit_size() ? *continuation() : *entry()); + __ cbnz(tmp2, L_skip); } + __ hlt(0); - // This is were the slowpath stub will return to or the code above will - // jump to if the checks are false + // Currently hot-patched to JUMP. + __ bind(L_fake_entry); + if (jump_when_state) { + __ cbnz(tmp2, L_skip); + } else { + __ cbz(tmp2, L_skip); + } + __ hlt(0); + + __ bind(L_skip); + + address check_end = __ pc(); + size_t actual_check_size = pointer_delta(check_end, check_start, Assembler::instruction_size); + assert(check_size == actual_check_size, "Must match: %zu != %zu", check_size, actual_check_size); +#endif + + // Emit the unconditional branch in the first version of the method. + // Let the rest of runtime figure out how to manage it. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ b(*L_target); +} + +void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp1, Register tmp2) { + Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); + patchable_jump_if_gc_state(masm, test_state, tmp1, tmp2, entry()); __ bind(*continuation()); } @@ -801,16 +888,15 @@ void ShenandoahBarrierStubC2::maybe_far_jump_if_zero(MacroAssembler& masm, Regis } void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { - Address gcstate(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING))); Address index(rthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); Address buffer(rthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); Label L_through, L_slowpath; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_load_ref_barrier) { - assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true"); - __ ldrb(_tmp1, gcstate); - __ cbz(_tmp1, L_through); + assert(L_done == nullptr, "Should be"); + char state_to_check = ShenandoahHeap::MARKING; + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_through); } // Fast-path: put object into buffer. @@ -860,20 +946,17 @@ void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { Label L_slow; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_keep_alive_barrier) { char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0); - Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check))); - __ ldrb(_tmp1, gc_state_fast); - maybe_far_jump_if_zero(masm, _tmp1); + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, continuation()); } // If weak references are being processed, weak/phantom loads need to go slow, // regardless of their cset status. if (_needs_load_ref_weak_barrier) { - Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS))); - __ ldrb(_tmp1, gc_state_fast); - __ cbnz(_tmp1, L_slow); + char state_to_check = ShenandoahHeap::WEAK_ROOTS; + patchable_jump_if_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_slow); } // Cset-check. Fall-through to slow if in collection set. diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index da5731ad19a4..4d12a53154e6 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -76,6 +76,13 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); + // Jumps hotpatching + static address parse_jump_address(address pc); + static void insert_patchable_nop(address pc); + static bool is_patchable_nop(address pc); + static void insert_patchable_jump(address pc, address target_pc); + static bool is_patchable_jump(address pc, address target_pc); + #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp index a92f8f5014cb..eba01191ba46 100644 --- a/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interpreterRT_aarch64.cpp @@ -168,7 +168,7 @@ void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { __ cbnz(temp(), L); __ mov(r0, zr); __ bind(L); - static_assert(sizeof(jobject) == wordSize, ""); + static_assert(sizeof(jobject) == wordSize); __ str(r0, Address(to(), next_stack_offset(sizeof(jobject)))); } } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index fa3217c0dbb5..50a56bb0ae94 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -3440,7 +3440,7 @@ void MacroAssembler::resolve_jobject(Register value, Register tmp1, Register tmp b(done); bind(tagged); - STATIC_ASSERT(JNIHandles::TypeTag::weak_global == 0b1); + static_assert(JNIHandles::TypeTag::weak_global == 0b1); tbnz(value, 0, weak_tagged); // Test for weak tag. // Resolve global handle @@ -3465,7 +3465,7 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis #ifdef ASSERT { - STATIC_ASSERT(JNIHandles::TypeTag::global == 0b10); + static_assert(JNIHandles::TypeTag::global == 0b10); Label valid_global_tag; tbnz(value, 1, valid_global_tag); // Test for global tag stop("non global jobject using resolve_global_jobject"); @@ -6041,6 +6041,7 @@ void MacroAssembler::remove_frame(int initial_framesize, bool needs_stack_repair ldr(rscratch1, Address(sp, sp_inc_offset)); add(sp, sp, rscratch1); ldp(rfp, lr, Address(post(sp, 2 * wordSize))); + authenticate_return_address(); } else { remove_frame(initial_framesize); } @@ -7012,10 +7013,6 @@ void MacroAssembler::verified_entry(Compile* C, int sp_inc) { if (C->needs_stack_repair()) { save_stack_increment(sp_inc, framesize); } - - if (VerifyStackAtCalls) { - Unimplemented(); - } } #endif // COMPILER2 @@ -7182,8 +7179,17 @@ int MacroAssembler::extend_stack_for_value_args(int args_on_stack) { sp_inc = align_up(sp_inc, StackAlignmentInBytes); assert(sp_inc > 0, "sanity"); - // Save a copy of the FP and LR here for deoptimization patching and frame walking + // Save a copy of the FP and LR here for deoptimization patching and frame + // walking. See remove_frame(). Sign LR #1 before spilling. Strip afterwards + // so build_frame() can use its normal path (it expects a raw LR and signs + // before spilling LR #2). Signing LR #2 is not strictly necessary. Reusing + // the signed LR #1 (PACIAZ, modifier zero) or storing a raw LR #2 (layout + // placeholder) would both work, but would need a special build_frame() path + // for stack repair, which adds code complexity. Current approach (strip + + // re-sign) keeps one generic build_frame(). + protect_return_address(); stp(rfp, lr, Address(pre(sp, -2 * wordSize))); + strip_return_address(); // Adjust the stack pointer. This will be repaired on return by MacroAssembler::remove_frame if (sp_inc < (1 << 9)) { diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 2a0dc58aa09c..fff80f01bb7d 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -584,7 +584,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, OopMapSet* oop_maps, int& frame_complete, int& frame_size_in_words, - bool alloc_inline_receiver) { + bool alloc_value_receiver) { if (requires_clinit_barrier) { assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; @@ -656,7 +656,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, __ mov(c_rarg0, rthread); __ mov(c_rarg1, rmethod); - __ mov(c_rarg2, (int64_t)alloc_inline_receiver); + __ mov(c_rarg2, (int64_t)alloc_value_receiver); __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::allocate_value_types))); __ blr(rscratch1); @@ -735,7 +735,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, int vt = 1; // write fields we get from compiled code in registers/stack // slots to the buffer: we know we are done with that value type - // argument when we hit the T_VOID that acts as an end of inline + // argument when we hit the T_VOID that acts as an end of value // type delimiter for this value type. Value types are flattened // so we might encounter embedded value types. Each entry in // sig_extended contains a field offset in the buffer. @@ -993,7 +993,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, if (regs_cc != regs_cc_ro) { // No class init barrier needed because method is guaranteed to be non-static gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); skip_fixup.reset(); } @@ -1001,7 +1001,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I] = __ pc(); entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ true); // Non-scalarized c2i adapter if (regs != regs_cc) { @@ -1011,7 +1011,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); } // The c2i adapters might safepoint and trigger a GC. The caller must make sure that diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 14cf4b089894..ba9f7bf8cf80 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5083,6 +5083,119 @@ class StubGenerator: public StubCodeGenerator { return start; } + void store_keccak_state(const Register a[], Register state) { + int i; + + for (i = 0; i < 24; i += 2) { + __ stp(a[i], a[i + 1], Address(state, i * wordSize)); + } + __ str(a[i], Address(state, i * wordSize)); + } + + void load_keccak_state(const Register a[], Register state) { + int i; + + for (i = 0; i < 24; i += 2) { + __ ldp(a[i], a[i + 1], Address(state, i * wordSize)); + } + __ ldr(a[i], Address(state, i * wordSize)); + } + + // Inputs: + // c_rarg0 - long[] state0 + // c_rarg1 - long[] state1 + address generate_double_keccak_gpr() { + 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, stub_id); + start = __ pc(); + + Register state0 = c_rarg0; + Register state1 = c_rarg1; + + // use r3.r17,r19..r28 to keep a0..a24. + // a0..a24 are respective locals from SHA3.java + const Register a[25] = { + r25, r26, r27, r3, r4, r5, r6, r7, rscratch1, rscratch2, r10, r11, r12, + r13, r14, r15, r16, r17, r28, r19, r20, r21, r22, r23, r24 }; + Register tmp0 = r0, tmp1 = r1, tmp2 = r2; + + Label rounds24_loop_0, rounds24_loop_1; + + const bool can_use_r18 = R18_RESERVED_ONLY(false) NOT_R18_RESERVED(true); + + bool can_use_fp = !PreserveFramePointer; + + __ enter(); + + // save the state addresses and callee-saved registers + auto saved_regs = RegSet::range(r19, r28) + state0 + state1; + + if (can_use_r18 && can_use_fp) { + saved_regs += r18_tls; + } + __ push(saved_regs, sp); + + // load state0 + load_keccak_state(a, state0); + + // 24 keccak rounds for state0 + __ fmovs(v0, 24.0); // float loop counter, + __ fmovs(v1, 1.0); // exact representation + + // load round_constants base + __ lea(lr, ExternalAddress((address) _double_keccak_round_consts)); + + __ BIND(rounds24_loop_0); + keccak_round_gpr(can_use_fp, can_use_r18, lr, a, tmp0, tmp1, tmp2); + __ fsubs(v0, v0, v1); + __ fcmps(v0, 0.0); + __ br(__ NE, rounds24_loop_0); + + // store state0 and load state1 + __ ldp(state0, state1, Address(sp)); + store_keccak_state(a, state0); + load_keccak_state(a, state1); + + // 24 keccak rounds for state1 + __ fmovs(v0, 24.0); // reset float loop counter, + + // load round_constants base + __ lea(lr, ExternalAddress((address) _double_keccak_round_consts)); + + __ BIND(rounds24_loop_1); + keccak_round_gpr(can_use_fp, can_use_r18, lr, a, tmp0, tmp1, tmp2); + __ fsubs(v0, v0, v1); + __ fcmps(v0, 0.0); + __ br(__ NE, rounds24_loop_1); + + __ ldr(state1, Address(sp, 8)); + + // store state1 + store_keccak_state(a, state1); + + // restore callee-saved registers + __ pop(saved_regs, sp); + __ mov(rfp, sp); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov(r0, zr); // return 0 + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + // ChaCha20 block function. This version parallelizes the 32-bit // state elements on each of 16 vectors, producing 4 blocks of // keystream at a time. @@ -8811,105 +8924,133 @@ class StubGenerator: public StubCodeGenerator { __ eor(a4, a4, tmp2); } + // The Keccak algorithm needs to read and update 25 state registers + // (`a[0]` ... `a[24]`) and maintain a round-constant cursor, `rc`. + // It also computes up to 5 intermediate values that are all live at the + // same time across some part of the computation, `tmp0` ... `tmp4`. + // This means that up to 31 independent values are live at once, + // preferably stored in registers to avoid having to push and pop + // intermediate results. Since `sp` cannot be overwritten this means + // that for the best performance every other gpr register needs to be + // used to store this data. + + // When `r18` is not reserved and frame pointers need not be preserved then + // `rfp` and `r18` are aliased to temporaries `tmp3` and `tmp4`, respectively. + // All other register mappings are determined by the emitter. + + // If `r18` or `rfp` cannot be used then 2 of the 31 live register values are + // pushed to the stack after their values are consumed and then popped when + // needed, reducing the count of values that need to be held live in registers + // to 29. This frees the registers associated with the pushed data for reuse + // as temporaries. Luckily, the algorithm consumes two state-lanes, + // `a[4]` and `a[9]`, well before all 5 temporary values are live and only + // needs to reuse those specific `a[]` lanes after two of these temporary + // values, `tmp3` and `tmp4` are no longer live. In this case, the values in + // `a[4]` and `a[9]` can be pushed once early during the computation to free + // two registers for use as temporaries and popped once later in the + // computation when those two temporaries are no longer needed. In + // this case `a[4]` and `a[9]` are aliased to temporaries `tmp3` + // and `tmp4`, respectively, for the segment of the computation that lies + // between the push and pop. + + // When `r18` and `rfp` are available then `saved_regs` will be empty. In + // this case push/pop will not emit any instructions, introducing no spills. void keccak_round_gpr(bool can_use_fp, bool can_use_r18, Register rc, - Register a0, Register a1, Register a2, Register a3, Register a4, - Register a5, Register a6, Register a7, Register a8, Register a9, - Register a10, Register a11, Register a12, Register a13, Register a14, - Register a15, Register a16, Register a17, Register a18, Register a19, - Register a20, Register a21, Register a22, Register a23, Register a24, - Register tmp0, Register tmp1, Register tmp2) { - __ eor3(tmp1, a4, a9, a14); - __ eor3(tmp0, tmp1, a19, a24); // tmp0 = a4^a9^a14^a19^a24 = c4 - __ eor3(tmp2, a1, a6, a11); - __ eor3(tmp1, tmp2, a16, a21); // tmp1 = a1^a6^a11^a16^a21 = c1 + const Register a[], Register tmp0, Register tmp1, + Register tmp2) { + __ eor3(tmp1, a[4], a[9], a[14]); + __ eor3(tmp0, tmp1, a[19], a[24]); // tmp0 = a4^a9^a14^a19^a24 = c4 + __ eor3(tmp2, a[1], a[6], a[11]); + __ eor3(tmp1, tmp2, a[16], a[21]); // tmp1 = a1^a6^a11^a16^a21 = c1 __ rax1(tmp2, tmp0, tmp1); // d0 { Register tmp3, tmp4; + RegSet saved_regs; + if (can_use_fp && can_use_r18) { tmp3 = rfp; tmp4 = r18_tls; } else { - tmp3 = a4; - tmp4 = a9; - __ stp(tmp3, tmp4, __ pre(sp, -16)); + tmp3 = a[4]; + tmp4 = a[9]; + saved_regs = RegSet::of(tmp3, tmp4); } - - __ eor3(tmp3, a0, a5, a10); - __ eor3(tmp4, tmp3, a15, a20); // tmp4 = a0^a5^a10^a15^a20 = c0 - __ eor(a0, a0, tmp2); - __ eor(a5, a5, tmp2); - __ eor(a10, a10, tmp2); - __ eor(a15, a15, tmp2); - __ eor(a20, a20, tmp2); // d0(tmp2) - __ eor3(tmp3, a2, a7, a12); - __ eor3(tmp2, tmp3, a17, a22); // tmp2 = a2^a7^a12^a17^a22 = c2 + __ push(saved_regs, sp); + + __ eor3(tmp3, a[0], a[5], a[10]); + __ eor3(tmp4, tmp3, a[15], a[20]); // tmp4 = a0^a5^a10^a15^a20 = c0 + __ eor(a[0], a[0], tmp2); + __ eor(a[5], a[5], tmp2); + __ eor(a[10], a[10], tmp2); + __ eor(a[15], a[15], tmp2); + __ eor(a[20], a[20], tmp2); // d0(tmp2) + __ eor3(tmp3, a[2], a[7], a[12]); + __ eor3(tmp2, tmp3, a[17], a[22]); // tmp2 = a2^a7^a12^a17^a22 = c2 __ rax1(tmp3, tmp4, tmp2); // d1 - __ eor(a1, a1, tmp3); - __ eor(a6, a6, tmp3); - __ eor(a11, a11, tmp3); - __ eor(a16, a16, tmp3); - __ eor(a21, a21, tmp3); // d1(tmp3) + __ eor(a[1], a[1], tmp3); + __ eor(a[6], a[6], tmp3); + __ eor(a[11], a[11], tmp3); + __ eor(a[16], a[16], tmp3); + __ eor(a[21], a[21], tmp3); // d1(tmp3) __ rax1(tmp3, tmp2, tmp0); // d3 - __ eor3(tmp2, a3, a8, a13); - __ eor3(tmp0, tmp2, a18, a23); // tmp0 = a3^a8^a13^a18^a23 = c3 - __ eor(a3, a3, tmp3); - __ eor(a8, a8, tmp3); - __ eor(a13, a13, tmp3); - __ eor(a18, a18, tmp3); - __ eor(a23, a23, tmp3); + __ eor3(tmp2, a[3], a[8], a[13]); + __ eor3(tmp0, tmp2, a[18], a[23]); // tmp0 = a3^a8^a13^a18^a23 = c3 + __ eor(a[3], a[3], tmp3); + __ eor(a[8], a[8], tmp3); + __ eor(a[13], a[13], tmp3); + __ eor(a[18], a[18], tmp3); + __ eor(a[23], a[23], tmp3); __ rax1(tmp2, tmp1, tmp0); // d2 - __ eor(a2, a2, tmp2); - __ eor(a7, a7, tmp2); - __ eor(a12, a12, tmp2); + __ eor(a[2], a[2], tmp2); + __ eor(a[7], a[7], tmp2); + __ eor(a[12], a[12], tmp2); __ rax1(tmp0, tmp0, tmp4); // d4 - if (!can_use_fp || !can_use_r18) { - __ ldp(tmp3, tmp4, __ post(sp, 16)); - } - __ eor(a17, a17, tmp2); - __ eor(a22, a22, tmp2); - __ eor(a4, a4, tmp0); - __ eor(a9, a9, tmp0); - __ eor(a14, a14, tmp0); - __ eor(a19, a19, tmp0); - __ eor(a24, a24, tmp0); - } - - __ rol(tmp0, a10, 3); - __ rol(a10, a1, 1); - __ rol(a1, a6, 44); - __ rol(a6, a9, 20); - __ rol(a9, a22, 61); - __ rol(a22, a14, 39); - __ rol(a14, a20, 18); - __ rol(a20, a2, 62); - __ rol(a2, a12, 43); - __ rol(a12, a13, 25); - __ rol(a13, a19, 8) ; - __ rol(a19, a23, 56); - __ rol(a23, a15, 41); - __ rol(a15, a4, 27); - __ rol(a4, a24, 14); - __ rol(a24, a21, 2); - __ rol(a21, a8, 55); - __ rol(a8, a16, 45); - __ rol(a16, a5, 36); - __ rol(a5, a3, 28); - __ rol(a3, a18, 21); - __ rol(a18, a17, 15); - __ rol(a17, a11, 10); - __ rol(a11, a7, 6); - __ mov(a7, tmp0); - - bcax5(a0, a1, a2, a3, a4, tmp0, tmp1, tmp2); - bcax5(a5, a6, a7, a8, a9, tmp0, tmp1, tmp2); - bcax5(a10, a11, a12, a13, a14, tmp0, tmp1, tmp2); - bcax5(a15, a16, a17, a18, a19, tmp0, tmp1, tmp2); - bcax5(a20, a21, a22, a23, a24, tmp0, tmp1, tmp2); + __ pop(saved_regs, sp); + + __ eor(a[17], a[17], tmp2); + __ eor(a[22], a[22], tmp2); + __ eor(a[4], a[4], tmp0); + __ eor(a[9], a[9], tmp0); + __ eor(a[14], a[14], tmp0); + __ eor(a[19], a[19], tmp0); + __ eor(a[24], a[24], tmp0); + } + + __ rol(tmp0, a[10], 3); + __ rol(a[10], a[1], 1); + __ rol(a[1], a[6], 44); + __ rol(a[6], a[9], 20); + __ rol(a[9], a[22], 61); + __ rol(a[22], a[14], 39); + __ rol(a[14], a[20], 18); + __ rol(a[20], a[2], 62); + __ rol(a[2], a[12], 43); + __ rol(a[12], a[13], 25); + __ rol(a[13], a[19], 8) ; + __ rol(a[19], a[23], 56); + __ rol(a[23], a[15], 41); + __ rol(a[15], a[4], 27); + __ rol(a[4], a[24], 14); + __ rol(a[24], a[21], 2); + __ rol(a[21], a[8], 55); + __ rol(a[8], a[16], 45); + __ rol(a[16], a[5], 36); + __ rol(a[5], a[3], 28); + __ rol(a[3], a[18], 21); + __ rol(a[18], a[17], 15); + __ rol(a[17], a[11], 10); + __ rol(a[11], a[7], 6); + __ mov(a[7], tmp0); + + bcax5(a[0], a[1], a[2], a[3], a[4], tmp0, tmp1, tmp2); + bcax5(a[5], a[6], a[7], a[8], a[9], tmp0, tmp1, tmp2); + bcax5(a[10], a[11], a[12], a[13], a[14], tmp0, tmp1, tmp2); + bcax5(a[15], a[16], a[17], a[18], a[19], tmp0, tmp1, tmp2); + bcax5(a[20], a[21], a[22], a[23], a[24], tmp0, tmp1, tmp2); __ ldr(tmp1, __ post(rc, 8)); - __ eor(a0, a0, tmp1); - + __ eor(a[0], a[0], tmp1); } // Arguments: @@ -8951,110 +9092,71 @@ class StubGenerator: public StubCodeGenerator { // use r3.r17,r19..r28 to keep a0..a24. // a0..a24 are respective locals from SHA3.java - Register a0 = r25, - a1 = r26, - a2 = r27, - a3 = r3, - a4 = r4, - a5 = r5, - a6 = r6, - a7 = r7, - a8 = rscratch1, // r8 - a9 = rscratch2, // r9 - a10 = r10, - a11 = r11, - a12 = r12, - a13 = r13, - a14 = r14, - a15 = r15, - a16 = r16, - a17 = r17, - a18 = r28, - a19 = r19, - a20 = r20, - a21 = r21, - a22 = r22, - a23 = r23, - a24 = r24; - - Register tmp0 = block_size, tmp1 = buf, tmp2 = state, tmp3 = r30; + const Register a[25] = { + r25, r26, r27, r3, r4, r5, r6, r7, rscratch1, rscratch2, r10, r11, r12, + r13, r14, r15, r16, r17, r28, r19, r20, r21, r22, r23, r24 }; + Register tmp0 = block_size, tmp1 = buf, tmp2 = state, tmp3 = lr; Label sha3_loop, rounds24_preloop, loop_body; Label sha3_512_or_sha3_384, shake128; - bool can_use_r18 = false; -#ifndef R18_RESERVED - can_use_r18 = true; -#endif + const bool can_use_r18 = R18_RESERVED_ONLY(false) NOT_R18_RESERVED(true); + bool can_use_fp = !PreserveFramePointer; __ enter(); // save almost all yet unsaved gpr registers on stack - __ str(block_size, __ pre(sp, -128)); + auto saved_regs = RegSet::range(r19, r28); + __ push(saved_regs, sp); + __ sub(sp, sp, 48); + + __ str(block_size, sp); if (multi_block) { __ stpw(ofs, limit, Address(sp, 8)); } - // 8 bytes at sp+16 will be used to keep buf - __ stp(r19, r20, Address(sp, 32)); - __ stp(r21, r22, Address(sp, 48)); - __ stp(r23, r24, Address(sp, 64)); - __ stp(r25, r26, Address(sp, 80)); - __ stp(r27, r28, Address(sp, 96)); if (can_use_r18 && can_use_fp) { - __ stp(r18_tls, state, Address(sp, 112)); + __ stp(r18_tls, state, Address(sp, 24)); } else { - __ str(state, Address(sp, 112)); + __ str(state, Address(sp, 24)); } // begin sha3 calculations: loading a0..a24 from state arrary - __ ldp(a0, a1, state); - __ ldp(a2, a3, Address(state, 16)); - __ ldp(a4, a5, Address(state, 32)); - __ ldp(a6, a7, Address(state, 48)); - __ ldp(a8, a9, Address(state, 64)); - __ ldp(a10, a11, Address(state, 80)); - __ ldp(a12, a13, Address(state, 96)); - __ ldp(a14, a15, Address(state, 112)); - __ ldp(a16, a17, Address(state, 128)); - __ ldp(a18, a19, Address(state, 144)); - __ ldp(a20, a21, Address(state, 160)); - __ ldp(a22, a23, Address(state, 176)); - __ ldr(a24, Address(state, 192)); + load_keccak_state(a, state); __ BIND(sha3_loop); // load input __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a0, a0, tmp3); - __ eor(a1, a1, tmp2); + __ eor(a[0], a[0], tmp3); + __ eor(a[1], a[1], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a2, a2, tmp3); - __ eor(a3, a3, tmp2); + __ eor(a[2], a[2], tmp3); + __ eor(a[3], a[3], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a4, a4, tmp3); - __ eor(a5, a5, tmp2); + __ eor(a[4], a[4], tmp3); + __ eor(a[5], a[5], tmp2); __ ldr(tmp3, __ post(buf, 8)); - __ eor(a6, a6, tmp3); + __ eor(a[6], a[6], tmp3); // block_size == 72, SHA3-512; block_size == 104, SHA3-384 __ tbz(block_size, 7, sha3_512_or_sha3_384); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a7, a7, tmp3); - __ eor(a8, a8, tmp2); + __ eor(a[7], a[7], tmp3); + __ eor(a[8], a[8], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a9, a9, tmp3); - __ eor(a10, a10, tmp2); + __ eor(a[9], a[9], tmp3); + __ eor(a[10], a[10], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a11, a11, tmp3); - __ eor(a12, a12, tmp2); + __ eor(a[11], a[11], tmp3); + __ eor(a[12], a[12], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a13, a13, tmp3); - __ eor(a14, a14, tmp2); + __ eor(a[13], a[13], tmp3); + __ eor(a[14], a[14], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a15, a15, tmp3); - __ eor(a16, a16, tmp2); + __ eor(a[15], a[15], tmp3); + __ eor(a[16], a[16], tmp2); // block_size == 136, bit4 == 0 and bit5 == 0, SHA3-256 or SHAKE256 __ andw(tmp2, block_size, 48); @@ -9062,31 +9164,31 @@ class StubGenerator: public StubCodeGenerator { __ tbnz(block_size, 5, shake128); // block_size == 144, bit5 == 0, SHA3-244 __ ldr(tmp3, __ post(buf, 8)); - __ eor(a17, a17, tmp3); + __ eor(a[17], a[17], tmp3); __ b(rounds24_preloop); __ BIND(shake128); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a17, a17, tmp3); - __ eor(a18, a18, tmp2); + __ eor(a[17], a[17], tmp3); + __ eor(a[18], a[18], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a19, a19, tmp3); - __ eor(a20, a20, tmp2); + __ eor(a[19], a[19], tmp3); + __ eor(a[20], a[20], tmp2); __ b(rounds24_preloop); // block_size == 168, SHAKE128 __ BIND(sha3_512_or_sha3_384); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a7, a7, tmp3); - __ eor(a8, a8, tmp2); + __ eor(a[7], a[7], tmp3); + __ eor(a[8], a[8], tmp2); __ tbz(block_size, 5, rounds24_preloop); // SHA3-512 // SHA3-384 __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a9, a9, tmp3); - __ eor(a10, a10, tmp2); + __ eor(a[9], a[9], tmp3); + __ eor(a[10], a[10], tmp2); __ ldp(tmp3, tmp2, __ post(buf, 16)); - __ eor(a11, a11, tmp3); - __ eor(a12, a12, tmp2); + __ eor(a[11], a[11], tmp3); + __ eor(a[12], a[12], tmp2); __ BIND(rounds24_preloop); __ fmovs(v0, 24.0); // float loop counter, @@ -9096,10 +9198,7 @@ class StubGenerator: public StubCodeGenerator { __ lea(tmp3, ExternalAddress((address) _sha3_round_consts)); __ BIND(loop_body); - keccak_round_gpr(can_use_fp, can_use_r18, tmp3, - a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, - a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, - tmp0, tmp1, tmp2); + keccak_round_gpr(can_use_fp, can_use_r18, tmp3, a, tmp0, tmp1, tmp2); __ fsubs(v0, v0, v1); __ fcmps(v0, 0.0); __ br(__ NE, loop_body); @@ -9115,33 +9214,19 @@ class StubGenerator: public StubCodeGenerator { __ movw(c_rarg0, tmp2); // return offset } if (can_use_fp && can_use_r18) { - __ ldp(r18_tls, state, Address(sp, 112)); + __ ldp(r18_tls, state, Address(sp, 24)); } else { - __ ldr(state, Address(sp, 112)); + __ ldr(state, Address(sp, 24)); } + // save calculated sha3 state - __ stp(a0, a1, Address(state)); - __ stp(a2, a3, Address(state, 16)); - __ stp(a4, a5, Address(state, 32)); - __ stp(a6, a7, Address(state, 48)); - __ stp(a8, a9, Address(state, 64)); - __ stp(a10, a11, Address(state, 80)); - __ stp(a12, a13, Address(state, 96)); - __ stp(a14, a15, Address(state, 112)); - __ stp(a16, a17, Address(state, 128)); - __ stp(a18, a19, Address(state, 144)); - __ stp(a20, a21, Address(state, 160)); - __ stp(a22, a23, Address(state, 176)); - __ str(a24, Address(state, 192)); + store_keccak_state(a, state); // restore required registers from stack - __ ldp(r19, r20, Address(sp, 32)); - __ ldp(r21, r22, Address(sp, 48)); - __ ldp(r23, r24, Address(sp, 64)); - __ ldp(r25, r26, Address(sp, 80)); - __ ldp(r27, r28, Address(sp, 96)); + __ add(sp, sp, 48); + __ pop(saved_regs, sp); if (can_use_fp && can_use_r18) { - __ add(rfp, sp, 128); // leave() will copy rfp to sp below + __ mov(rfp, sp); // leave() will copy rfp to sp below } // else no need to recalculate rfp, since it wasn't changed __ leave(); @@ -12502,7 +12587,7 @@ class StubGenerator: public StubCodeGenerator { } // we're now on the yield frame (which is in an address above us b/c rsp has been pushed down) - __ sub(sp, rscratch2, 2*wordSize); // now pointing to rfp spill + __ sub(sp, rscratch2, 2 * wordSize); // now pointing to rfp spill __ mov(rfp, sp); if (return_barrier_exception) { @@ -13943,6 +14028,7 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_sha3_implCompress = generate_sha3_implCompress(StubId::stubgen_sha3_implCompress_id); StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress(StubId::stubgen_sha3_implCompressMB_id); } else if (UseSHA3Intrinsics) { + StubRoutines::_double_keccak = generate_double_keccak_gpr(); StubRoutines::_sha3_implCompress = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompress_id); StubRoutines::_sha3_implCompressMB = generate_sha3_implCompress_gpr(StubId::stubgen_sha3_implCompressMB_id); } diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 4f68356a117c..9ca01c6d990c 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -3043,7 +3043,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr do_oop_store(_masm, field, r0, IN_HEAP); __ b(Done); } else { - Label null_free_reference, is_flat, rewrite_inline; + Label null_free_reference, is_flat, rewrite_value; __ test_field_is_flat(flags, noreg /* temp */, is_flat); __ test_field_is_null_free_value_type(flags, noreg /* temp */, null_free_reference); pop_and_check_object(obj); @@ -3061,11 +3061,11 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr // Store into the field // Clobbers: r10, r11, r3 do_oop_store(_masm, field, r0, IN_HEAP); - __ b(rewrite_inline); + __ b(rewrite_value); __ bind(is_flat); pop_and_check_object(r7); __ write_flat_field(cache, off, index, flags, r7); - __ bind(rewrite_inline); + __ bind(rewrite_value); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_vputfield, bc, r19, true, byte_no); } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 0683202515c8..ac4d004740b3 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -174,7 +174,7 @@ class VM_Version : public Abstract_VM_Version { MAX_CPU_FEATURES }; - STATIC_ASSERT(sizeof(_features) * BitsPerByte >= MAX_CPU_FEATURES); + static_assert(sizeof(_features) * BitsPerByte >= MAX_CPU_FEATURES); static const char* _features_names[]; diff --git a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp index 9218135bb386..af84763d974a 100644 --- a/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vtableStubs_aarch64.cpp @@ -77,7 +77,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) #endif // get receiver (need to skip return address on top of stack) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); // get receiver klass address npe_addr = __ pc(); @@ -169,7 +169,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) #endif // get receiver (need to skip return address on top of stack) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); // Entry arguments: // rscratch2: CompiledICData diff --git a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp index f93eea15464c..d110c8ef0e5b 100644 --- a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp @@ -944,7 +944,7 @@ void LIRGenerator::do_NewInstance(NewInstance* x) { LIR_Opr tmp2 = new_register(objectType); LIR_Opr tmp3 = FrameMap::LR_oop_opr; - new_instance(reg, x->klass(), x->is_unresolved(), /* allow_inline */ false, tmp1, tmp2, tmp3, + new_instance(reg, x->klass(), x->is_unresolved(), /* allow_value */ false, tmp1, tmp2, tmp3, LIR_OprFact::illegalOpr, klass_reg, info); LIR_Opr result = rlock_result(x); diff --git a/src/hotspot/cpu/arm/macroAssembler_arm.cpp b/src/hotspot/cpu/arm/macroAssembler_arm.cpp index 39f0ac2e0dbb..5e910fb5f4a4 100644 --- a/src/hotspot/cpu/arm/macroAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/macroAssembler_arm.cpp @@ -1706,8 +1706,8 @@ void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators, void MacroAssembler::safepoint_poll(Register tmp1, Label& slow_path) { ldr_u32(tmp1, Address(Rthread, JavaThread::polling_word_offset())); - tst(tmp1, exact_log2(SafepointMechanism::poll_bit())); - b(slow_path, eq); + tst(tmp1, SafepointMechanism::poll_bit()); + b(slow_path, ne); } void MacroAssembler::get_polling_page(Register dest) { diff --git a/src/hotspot/cpu/arm/vtableStubs_arm.cpp b/src/hotspot/cpu/arm/vtableStubs_arm.cpp index 3f34fa76969e..e69f208ea33d 100644 --- a/src/hotspot/cpu/arm/vtableStubs_arm.cpp +++ b/src/hotspot/cpu/arm/vtableStubs_arm.cpp @@ -72,7 +72,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == R0->as_VMReg(), "receiver expected in R0"); + assert(SharedRuntime::name_for_receiver() == R0->as_VMReg(), "receiver expected in R0"); const Register tmp = Rtemp; // Rtemp OK, should be free at call sites @@ -141,7 +141,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == R0->as_VMReg(), "receiver expected in R0"); + assert(SharedRuntime::name_for_receiver() == R0->as_VMReg(), "receiver expected in R0"); // R0-R3 / R0-R7 registers hold the arguments and cannot be spoiled const Register Rclass = R4; diff --git a/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp b/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp index c4529b0eb1ab..f0e7d226c4ba 100644 --- a/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp +++ b/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp @@ -108,8 +108,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D result; @@ -135,8 +135,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D result; @@ -165,7 +165,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); // Note that xchg doesn't necessarily do an acquire // (see synchronizer.cpp). @@ -201,7 +201,7 @@ template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); // Note that xchg doesn't necessarily do an acquire // (see synchronizer.cpp). @@ -238,7 +238,7 @@ inline T AtomicAccess::PlatformCmpxchg<1>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(1 == sizeof(T)); + static_assert(1 == sizeof(T)); // Note that cmpxchg guarantees a two-way memory barrier across // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not @@ -286,7 +286,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); // Note that cmpxchg guarantees a two-way memory barrier across // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not @@ -332,7 +332,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); // Note that cmpxchg guarantees a two-way memory barrier across // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not @@ -389,7 +389,7 @@ class AtomicAccess::PlatformBitops<4, true> { public: template T fetch_then_and(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old_value, result; pre_membar(order); @@ -411,7 +411,7 @@ class AtomicAccess::PlatformBitops<4, true> { template T fetch_then_or(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old_value, result; pre_membar(order); @@ -433,7 +433,7 @@ class AtomicAccess::PlatformBitops<4, true> { template T fetch_then_xor(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old_value, result; pre_membar(order); @@ -455,7 +455,7 @@ class AtomicAccess::PlatformBitops<4, true> { template T and_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T result; pre_membar(order); @@ -476,7 +476,7 @@ class AtomicAccess::PlatformBitops<4, true> { template T or_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T result; pre_membar(order); @@ -497,7 +497,7 @@ class AtomicAccess::PlatformBitops<4, true> { template T xor_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T result; pre_membar(order); @@ -522,7 +522,7 @@ class AtomicAccess::PlatformBitops<8, true> { public: template T fetch_then_and(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old_value, result; pre_membar(order); @@ -544,7 +544,7 @@ class AtomicAccess::PlatformBitops<8, true> { template T fetch_then_or(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old_value, result; pre_membar(order); @@ -566,7 +566,7 @@ class AtomicAccess::PlatformBitops<8, true> { template T fetch_then_xor(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old_value, result; pre_membar(order); @@ -588,7 +588,7 @@ class AtomicAccess::PlatformBitops<8, true> { template T and_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T result; pre_membar(order); @@ -609,7 +609,7 @@ class AtomicAccess::PlatformBitops<8, true> { template T or_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T result; pre_membar(order); @@ -630,7 +630,7 @@ class AtomicAccess::PlatformBitops<8, true> { template T xor_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T result; pre_membar(order); diff --git a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp index c329a89850b9..2709266c6629 100644 --- a/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_Runtime1_ppc.cpp @@ -459,7 +459,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { break; case StubId::c1_new_null_free_array_id: __ cmpwi(CR0, R0, Klass::_lh_array_tag_flat_value); // the array can be a flat array. - __ cmpwi(CR1, R0, Klass::_lh_array_tag_ref_value); // the array cannot be a flat array (due to the InlineArrayElementMaxFlatSize, etc.) + __ cmpwi(CR1, R0, Klass::_lh_array_tag_ref_value); // the array cannot be a flat array. __ cror(CR0, Assembler::equal, CR1, Assembler::equal); __ asm_assert_eq("assert(is an object or value type array klass)"); break; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 7dbb01822661..a393f6fbb118 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -34,10 +34,13 @@ #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "interpreter/interpreter.hpp" #include "macroAssembler_ppc.hpp" +#include "nativeInst_ppc.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/globalDefinitions.hpp" @@ -49,6 +52,7 @@ #endif #ifdef COMPILER2 #include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#include "opto/output.hpp" #endif #define __ masm-> @@ -713,6 +717,38 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #undef __ +address ShenandoahBarrierSetAssembler::parse_jump_address(address pc) { + NativeInstruction* ni = nativeInstruction_at(pc); + assert(ni->is_jump(), "Must be"); + NativeGeneralJump* jmp = nativeGeneralJump_at(pc); + return jmp->jump_destination(); +} + +static uint32_t encode_patchable_nop() { + return 0x60000000; +} + +void ShenandoahBarrierSetAssembler::insert_patchable_nop(address pc) { + *((uint32_t*)pc) = encode_patchable_nop(); + ICache::invalidate_word(pc); +} + +void ShenandoahBarrierSetAssembler::insert_patchable_jump(address pc, address target_pc) { + CodeBuffer cb(pc, BytesPerInstWord + 1); + MacroAssembler a(&cb); + a.b(target_pc); + ICache::invalidate_word(pc); +} + +bool ShenandoahBarrierSetAssembler::is_patchable_nop(address pc) { + return *((uint32_t*)pc) == encode_patchable_nop(); +} + +bool ShenandoahBarrierSetAssembler::is_patchable_jump(address pc, address target_pc) { + NativeInstruction* ni = nativeInstruction_at(pc); + return ni->is_jump() && nativeGeneralJump_at(pc)->jump_destination() == target_pc; +} + #ifdef COMPILER1 #define __ ce->masm()-> @@ -949,14 +985,24 @@ void ShenandoahBarrierStubC2::cardtable(MacroAssembler& masm, Address address, R __ stbx(R0, tmp2, tmp1); } -void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) { - Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); +void ShenandoahBarrierStubC2::patchable_jump(MacroAssembler& masm, const char gc_state, bool jump_when_state, Register tmp1, Register tmp2, Label* L_target) { + PhaseOutput* const output = Compile::current()->output(); + if (output->in_scratch_emit_size()) { + // Avoid binding L_target in scratch emits. + // We know the patched check is exactly one instruction long. + __ nop(); + return; + } + + // Emit the unconditional branch in the first version of the method. + // Let the rest of runtime figure out how to manage it. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ b(*L_target); +} - __ lbz(tmp, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state)), R16_thread); - __ cmpdi(CR0, tmp, 0); - // Branch to entry if not equal - __ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CR0, Assembler::equal), *entry()); - // This is were the slowpath stub will return to +void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp1, Register tmp2) { + Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); + patchable_jump_if_gc_state(masm, test_state, tmp1, tmp2, entry()); __ bind(*continuation()); } @@ -1006,17 +1052,15 @@ void ShenandoahBarrierStubC2::maybe_far_jump_if_zero(MacroAssembler& masm, Regis } void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { - const int gcstate_offset = in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING)); const int index_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()); const int buffer_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()); Label L_through, L_slowpath; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_load_ref_barrier) { - assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true"); - __ lbz(_tmp1, gcstate_offset, R16_thread); - __ cmpdi(CR0, _tmp1, 0); - __ beq(CR0, L_through); + assert(L_done == nullptr, "Should be"); + char state_to_check = ShenandoahHeap::MARKING; + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_through); } // Fast-path: put object into buffer. @@ -1065,19 +1109,17 @@ void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { Label L_slow; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_keep_alive_barrier) { char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0); - __ lbz(_tmp1, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check)), R16_thread); - maybe_far_jump_if_zero(masm, _tmp1); + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, continuation()); } // If weak references are being processed, weak/phantom loads need to go slow, // regardless of their cset status. if (_needs_load_ref_weak_barrier) { - __ lbz(_tmp1, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS)), R16_thread); - __ cmpdi(CR0, _tmp1, 0); - __ bne(CR0, L_slow); + char state_to_check = ShenandoahHeap::WEAK_ROOTS; + patchable_jump_if_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_slow); } // Cset-check. Fall-through to slow if in collection set. diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 0784c8b7148f..9513e4b314d4 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -127,6 +127,13 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void check_oop(MacroAssembler *masm, Register obj, const char* msg); + // Jumps hotpatching + static address parse_jump_address(address pc); + static void insert_patchable_nop(address pc); + static bool is_patchable_nop(address pc); + static void insert_patchable_jump(address pc, address target_pc); + static bool is_patchable_jump(address pc, address target_pc); + #ifdef COMPILER2 // Entry points from Matcher void load_c2(const MachNode* node, MacroAssembler* masm, Register dst, Register addr, int disp, Register tmp1, Register tmp2, bool narrow, bool acquire); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 9afa32bc364e..7bec14d60072 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -4829,8 +4829,8 @@ void MacroAssembler::atomically_flip_locked_state(bool is_unlock, Register obj, } bind(retry); - STATIC_ASSERT(markWord::fast_locked_value == 0); // Or need to change this! - STATIC_ASSERT(markWord::lock_neutral_value == 1); // Or need to change this! + static_assert(markWord::fast_locked_value == 0); // Or need to change this! + static_assert(markWord::lock_neutral_value == 1); // Or need to change this! if (!is_unlock) { ldarx(tmp, obj, MacroAssembler::cmpxchgx_hint_acquire_lock()); xori(tmp, tmp, markWord::lock_neutral_value); // flip lock-neutral bit diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 7f38025bdeea..d0474ca9ff2b 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -1623,15 +1623,15 @@ static enum RC rc_class(OptoReg::Name reg) { if (reg == OptoReg::Bad) return rc_bad; // We have 64 integer register halves, starting at index 0. - STATIC_ASSERT((int)ConcreteRegisterImpl::max_gpr == (int)MachRegisterNumbers::F0_num); + static_assert((int)ConcreteRegisterImpl::max_gpr == (int)MachRegisterNumbers::F0_num); if (reg < ConcreteRegisterImpl::max_gpr) return rc_int; // We have 64 floating-point register halves, starting at index 64. - STATIC_ASSERT((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VR0_num); + static_assert((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VR0_num); if (reg < ConcreteRegisterImpl::max_fpr) return rc_float; // We have 64 vector-scalar registers, starting at index 128. - STATIC_ASSERT((int)ConcreteRegisterImpl::max_vr == (int)MachRegisterNumbers::CR0_num); + static_assert((int)ConcreteRegisterImpl::max_vr == (int)MachRegisterNumbers::CR0_num); if (reg < ConcreteRegisterImpl::max_vr) return rc_vec; // Condition and special purpose registers are not allocated. We only accept stack from here. @@ -2957,27 +2957,6 @@ encode %{ assert(!(ra_->is_oop(this)), "sanity"); // This is not supposed to be GC'ed. %} - enc_class postalloc_expand_encode_oop_not_null(iRegNdst dst, iRegPdst src) %{ - - encodeP_subNode *n1 = new encodeP_subNode(); - n1->add_req(n_region, n_src); - n1->_opnds[0] = op_dst; - n1->_opnds[1] = op_src; - n1->_bottom_type = _bottom_type; - - encodeP_shiftNode *n2 = new encodeP_shiftNode(); - n2->add_req(n_region, n1); - n2->_opnds[0] = op_dst; - n2->_opnds[1] = op_dst; - n2->_bottom_type = _bottom_type; - ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - nodes->push(n1); - nodes->push(n2); - assert(!(ra_->is_oop(this)), "sanity"); // This is not supposed to be GC'ed. - %} - enc_class postalloc_expand_decode_oop(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ decodeN_shiftNode *n_shift = new decodeN_shiftNode(); cmpN_reg_imm0Node *n_compare = new cmpN_reg_imm0Node(); @@ -3020,29 +2999,6 @@ encode %{ %} - enc_class postalloc_expand_decode_oop_not_null(iRegPdst dst, iRegNsrc src) %{ - decodeN_shiftNode *n1 = new decodeN_shiftNode(); - n1->add_req(n_region, n_src); - n1->_opnds[0] = op_dst; - n1->_opnds[1] = op_src; - n1->_bottom_type = _bottom_type; - - decodeN_addNode *n2 = new decodeN_addNode(); - n2->add_req(n_region, n1); - n2->_opnds[0] = op_dst; - n2->_opnds[1] = op_dst; - n2->_bottom_type = _bottom_type; - ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); - - assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); - ra_->set_oop(n2, true); - - nodes->push(n1); - nodes->push(n2); - %} - - // This enc_class is needed so that scheduler gets proper // input mapping for latency computation. enc_class enc_andc(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ @@ -4672,7 +4628,9 @@ opclass memoryAlg16(indirect, indOffset16Alg16); opclass indirectMemory(indirect, indirectNarrow); // Special opclass for I and ConvL2I. -opclass iRegIsrc_iRegL2Isrc(iRegIsrc, iRegL2Isrc); +// To be preferred over iRegIsrc to elide ConvL2I nodes if present. +// Causes adlc issues when the operand appears in an effect clause. +opclass iRegIorL2I(iRegIsrc, iRegL2Isrc); // Operand classes to match encode and decode. iRegN_P2N is only used // for storeN. I have never seen an encode node elsewhere. @@ -6133,7 +6091,7 @@ instruct prefetch_alloc_no_offset(indirectMemory mem) %{ //----------Store Instructions------------------------------------------------- // Store Byte -instruct storeB(memory mem, iRegIsrc src) %{ +instruct storeB(memory mem, iRegIorL2I src) %{ match(Set mem (StoreB mem src)); ins_cost(MEMORY_REF_COST); @@ -6147,7 +6105,7 @@ instruct storeB(memory mem, iRegIsrc src) %{ %} // Store Char/Short -instruct storeC(memory mem, iRegIsrc src) %{ +instruct storeC(memory mem, iRegIorL2I src) %{ match(Set mem (StoreC mem src)); ins_cost(MEMORY_REF_COST); @@ -6161,7 +6119,7 @@ instruct storeC(memory mem, iRegIsrc src) %{ %} // Store Integer -instruct storeI(memory mem, iRegIsrc src) %{ +instruct storeI(memory mem, iRegIorL2I src) %{ match(Set mem (StoreI mem src)); ins_cost(MEMORY_REF_COST); @@ -6171,17 +6129,6 @@ instruct storeI(memory mem, iRegIsrc src) %{ ins_pipe(pipe_class_memory); %} -// ConvL2I + StoreI. -instruct storeI_convL2I(memory mem, iRegLsrc src) %{ - match(Set mem (StoreI mem (ConvL2I src))); - ins_cost(MEMORY_REF_COST); - - format %{ "STW l2i($src), $mem" %} - size(4); - ins_encode( enc_stw(src, mem) ); - ins_pipe(pipe_class_memory); -%} - // Store Long instruct storeL(memoryAlg4 mem, iRegLsrc src) %{ match(Set mem (StoreL mem src)); @@ -6310,7 +6257,7 @@ instruct encodeP_shift(iRegNdst dst, iRegNsrc src) %{ format %{ "SRDI $dst, $src, 3 \t// encode" %} size(4); ins_encode %{ - __ srdi($dst$$Register, $src$$Register, CompressedOops::shift() & 0x3f); + __ srdi($dst$$Register, $src$$Register, CompressedOops::shift()); %} ins_pipe(pipe_class_default); %} @@ -6385,14 +6332,18 @@ instruct encodeP_Ex(iRegNdst dst, flagsReg crx, iRegPsrc src) %{ %} // shift != 0, base != 0 -instruct encodeP_not_null_Ex(iRegNdst dst, iRegPsrc src) %{ +instruct encodeP_not_null(iRegNdst dst, iRegPsrc src) %{ match(Set dst (EncodeP src)); predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull && CompressedOops::shift() != 0 && CompressedOops::base_overlaps()); - format %{ "EncodeP $dst, $src\t// $src != Null, postalloc expanded" %} - postalloc_expand( postalloc_expand_encode_oop_not_null(dst, src) ); + format %{ "EncodeP $dst, $src\t// $src != Null" %} + ins_encode %{ + __ sub_const_optimized($dst$$Register, $src$$Register, CompressedOops::base(), R0); + __ srdi($dst$$Register, $dst$$Register, CompressedOops::shift()); + %} + ins_pipe(pipe_class_default); %} // shift != 0, base == 0 @@ -6405,7 +6356,7 @@ instruct encodeP_not_null_base_null(iRegNdst dst, iRegPsrc src) %{ format %{ "SRDI $dst, $src, #3 \t// encodeP, $src != nullptr" %} size(4); ins_encode %{ - __ srdi($dst$$Register, $src$$Register, CompressedOops::shift() & 0x3f); + __ srdi($dst$$Register, $src$$Register, CompressedOops::shift()); %} ins_pipe(pipe_class_default); %} @@ -6621,7 +6572,7 @@ instruct decodeN_Disjoint_isel_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ %} // src != 0, shift != 0, base != 0 -instruct decodeN_notNull_addBase_Ex(iRegPdst dst, iRegNsrc src) %{ +instruct decodeN_notNull_addBase(iRegPdst dst, iRegNsrc src) %{ match(Set dst (DecodeN src)); predicate((n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull || n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant) && @@ -6629,8 +6580,12 @@ instruct decodeN_notNull_addBase_Ex(iRegPdst dst, iRegNsrc src) %{ CompressedOops::base() != nullptr); ins_cost(2 * DEFAULT_COST); - format %{ "DecodeN $dst, $src \t// $src != nullptr, postalloc expanded" %} - postalloc_expand( postalloc_expand_decode_oop_not_null(dst, src)); + format %{ "DecodeN $dst, $src \t// $src != nullptr" %} + ins_encode %{ + __ sldi($dst$$Register, $src$$Register, CompressedOops::shift()); + __ add_const_optimized($dst$$Register, $dst$$Register, CompressedOops::base(), R0); + %} + ins_pipe(pipe_class_default); %} // Compressed OOPs with narrow_oop_shift == 0. @@ -6903,7 +6858,7 @@ instruct onspinwait() %{ //----------Conditional Move--------------------------------------------------- // Cmove using isel. -instruct cmovI_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegIdst dst, iRegIsrc src) %{ +instruct cmovI_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegIdst dst, iRegIorL2I src) %{ match(Set dst (CMoveI (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST); @@ -7068,7 +7023,7 @@ instruct cmovD_cmpF(cmpOp cop, regF op1, regF op2, regD dst, regD false_result, // Strong versions: -instruct compareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndSwapB mem_ptr (Binary src1 src2))); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "CMPXCHGB $res, $mem_ptr, $src1, $src2; as bool" %} @@ -7086,7 +7041,7 @@ instruct compareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc ins_pipe(pipe_class_default); %} -instruct compareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndSwapS mem_ptr (Binary src1 src2))); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "CMPXCHGH $res, $mem_ptr, $src1, $src2; as bool" %} @@ -7104,7 +7059,7 @@ instruct compareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc ins_pipe(pipe_class_default); %} -instruct compareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndSwapI mem_ptr (Binary src1 src2))); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %} @@ -7180,7 +7135,7 @@ instruct compareAndSwapP_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iRegPsrc // Weak versions: -instruct weakCompareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapB mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7194,7 +7149,7 @@ instruct weakCompareAndSwapB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct weakCompareAndSwapB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapB mem_ptr (Binary src1 src2))); predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst) ); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7208,7 +7163,7 @@ instruct weakCompareAndSwapB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, ins_pipe(pipe_class_default); %} -instruct weakCompareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapS mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7222,7 +7177,7 @@ instruct weakCompareAndSwapS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct weakCompareAndSwapS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapS mem_ptr (Binary src1 src2))); predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7236,7 +7191,7 @@ instruct weakCompareAndSwapS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, ins_pipe(pipe_class_default); %} -instruct weakCompareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapI mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7250,7 +7205,7 @@ instruct weakCompareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct weakCompareAndSwapI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct weakCompareAndSwapI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (WeakCompareAndSwapI mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); // TEMP_DEF to avoid jump @@ -7359,7 +7314,7 @@ instruct weakCompareAndSwapP_acq_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, // CompareAndExchange -instruct compareAndExchangeB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeB mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); @@ -7373,7 +7328,7 @@ instruct compareAndExchangeB_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct compareAndExchangeB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeB mem_ptr (Binary src1 src2))); predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); effect(TEMP_DEF res, TEMP cr0); @@ -7394,7 +7349,7 @@ instruct compareAndExchangeB_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, %} -instruct compareAndExchangeS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeS mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); @@ -7408,7 +7363,7 @@ instruct compareAndExchangeS_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct compareAndExchangeS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeS mem_ptr (Binary src1 src2))); predicate((((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst)); effect(TEMP_DEF res, TEMP cr0); @@ -7428,7 +7383,7 @@ instruct compareAndExchangeS_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, ins_pipe(pipe_class_default); %} -instruct compareAndExchangeI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeI mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() != MemNode::acquire && ((CompareAndSwapNode*)n)->order() != MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); @@ -7442,7 +7397,7 @@ instruct compareAndExchangeI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iReg ins_pipe(pipe_class_default); %} -instruct compareAndExchangeI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct compareAndExchangeI_acq_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set res (CompareAndExchangeI mem_ptr (Binary src1 src2))); predicate(((CompareAndSwapNode*)n)->order() == MemNode::acquire || ((CompareAndSwapNode*)n)->order() == MemNode::seqcst); effect(TEMP_DEF res, TEMP cr0); @@ -7568,7 +7523,7 @@ instruct compareAndExchangeP_acq_regP_regP_regP(iRegPdst res, iRegPdst mem_ptr, // Special RMW -instruct getAndAddB(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndAddB(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndAddB mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndAddB $res, $mem_ptr, $src" %} @@ -7584,7 +7539,7 @@ instruct getAndAddB(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr ins_pipe(pipe_class_default); %} -instruct getAndAddS(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndAddS(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndAddS mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndAddS $res, $mem_ptr, $src" %} @@ -7601,7 +7556,7 @@ instruct getAndAddS(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr %} -instruct getAndAddI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndAddI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndAddI mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndAddI $res, $mem_ptr, $src" %} @@ -7633,7 +7588,7 @@ instruct getAndAddL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src, flagsRegCR0 cr ins_pipe(pipe_class_default); %} -instruct getAndSetB(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndSetB(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndSetB mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndSetB $res, $mem_ptr, $src" %} @@ -7649,7 +7604,7 @@ instruct getAndSetB(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr ins_pipe(pipe_class_default); %} -instruct getAndSetS(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndSetS(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndSetS mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndSetS $res, $mem_ptr, $src" %} @@ -7666,7 +7621,7 @@ instruct getAndSetS(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr %} -instruct getAndSetI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src, flagsRegCR0 cr0) %{ +instruct getAndSetI(iRegIdst res, iRegPdst mem_ptr, iRegIorL2I src, flagsRegCR0 cr0) %{ match(Set res (GetAndSetI mem_ptr src)); effect(TEMP_DEF res, TEMP cr0); format %{ "GetAndSetI $res, $mem_ptr, $src" %} @@ -7736,7 +7691,7 @@ instruct getAndSetN(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src, flagsRegCR0 cr // Addition Instructions // Register Addition -instruct addI_reg_reg(iRegIdst dst, iRegIsrc_iRegL2Isrc src1, iRegIsrc_iRegL2Isrc src2) %{ +instruct addI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (AddI src1 src2)); format %{ "ADD $dst, $src1, $src2" %} size(4); @@ -7758,7 +7713,7 @@ instruct addI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct tree_addI_addI_addI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ +instruct tree_addI_addI_addI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, iRegIorL2I src3, iRegIorL2I src4) %{ match(Set dst (AddI (AddI (AddI src1 src2) src3) src4)); ins_cost(DEFAULT_COST*3); @@ -7773,7 +7728,7 @@ instruct tree_addI_addI_addI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc sr %} // Immediate Addition -instruct addI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ +instruct addI_reg_imm16(iRegIdst dst, iRegIorL2I src1, immI16 src2) %{ match(Set dst (AddI src1 src2)); format %{ "ADDI $dst, $src1, $src2" %} size(4); @@ -7784,7 +7739,7 @@ instruct addI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ %} // Immediate Addition with 16-bit shifted operand -instruct addI_reg_immhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2) %{ +instruct addI_reg_immhi16(iRegIdst dst, iRegIorL2I src1, immIhi16 src2) %{ match(Set dst (AddI src1 src2)); format %{ "ADDIS $dst, $src1, $src2" %} size(4); @@ -7795,7 +7750,7 @@ instruct addI_reg_immhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2) %{ %} // Immediate Addition using prefixed addi -instruct addI_reg_imm32(iRegIdst dst, iRegIsrc src1, immI32 src2) %{ +instruct addI_reg_imm32(iRegIdst dst, iRegIorL2I src1, immI32 src2) %{ match(Set dst (AddI src1 src2)); predicate(PowerArchitecturePPC64 >= 10); ins_cost(DEFAULT_COST+1); @@ -7958,7 +7913,7 @@ instruct addP_reg_imm34(iRegPdst dst, iRegP_N2P src1, immL34 src2) %{ // Subtraction Instructions // Register Subtraction -instruct subI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct subI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (SubI src1 src2)); format %{ "SUBF $dst, $src2, $src1" %} size(4); @@ -7973,7 +7928,7 @@ instruct subI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ // Don't try to use addi with - $src2$$constant since it can overflow when $src2$$constant == minI16. // SubI from constant (using subfic). -instruct subI_imm16_reg(iRegIdst dst, immI16 src1, iRegIsrc src2) %{ +instruct subI_imm16_reg(iRegIdst dst, immI16 src1, iRegIorL2I src2) %{ match(Set dst (SubI src1 src2)); format %{ "SUBI $dst, $src1, $src2" %} @@ -7999,7 +7954,7 @@ instruct signmask32I_regI(iRegIdst dst, iRegIsrc src) %{ ins_pipe(pipe_class_default); %} -instruct absI_reg_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct absI_reg_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (AbsI src)); ins_cost(DEFAULT_COST*3); @@ -8012,7 +7967,7 @@ instruct absI_reg_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct negI_regI(iRegIdst dst, immI_0 zero, iRegIsrc src2) %{ +instruct negI_regI(iRegIdst dst, immI_0 zero, iRegIorL2I src2) %{ match(Set dst (SubI zero src2)); format %{ "NEG $dst, $src2" %} size(4); @@ -8115,7 +8070,7 @@ instruct negI_con0_regL(iRegIdst dst, immL_0 zero, iRegLsrc src2) %{ // Integer Multiplication // Register Multiplication -instruct mulI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct mulI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (MulI src1 src2)); ins_cost(DEFAULT_COST); @@ -8128,7 +8083,7 @@ instruct mulI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Immediate Multiplication -instruct mulI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ +instruct mulI_reg_imm16(iRegIdst dst, iRegIorL2I src1, immI16 src2) %{ match(Set dst (MulI src1 src2)); ins_cost(DEFAULT_COST); @@ -8190,23 +8145,10 @@ instruct mulL_reg_imm16(iRegLdst dst, iRegLsrc src1, immL16 src2) %{ ins_pipe(pipe_class_default); %} -// Integer Division with Immediate -1: Negate. -instruct divI_reg_immIvalueMinus1(iRegIdst dst, iRegIsrc src1, immI_minus1 src2) %{ - match(Set dst (DivI src1 src2)); - ins_cost(DEFAULT_COST); - - format %{ "NEG $dst, $src1 \t// /-1" %} - size(4); - ins_encode %{ - __ neg($dst$$Register, $src1$$Register); - %} - ins_pipe(pipe_class_default); -%} - // Integer Division with constant, but not -1. // We should be able to improve this by checking the type of src2. // It might well be that src2 is known to be positive. -instruct divI_reg_regnotMinus1(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct divI_reg_regnotMinus1(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (DivI src1 src2)); predicate(n->in(2)->find_int_con(-1) != -1); // src2 is a constant, but not -1 ins_cost(2*DEFAULT_COST); @@ -8235,7 +8177,7 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ %} // Integer Division with Registers not containing constants. -instruct divI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct divI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (DivI src1 src2)); ins_cost(10*DEFAULT_COST); @@ -8305,7 +8247,7 @@ instruct divL_reg_reg_Ex(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ %} // Integer Remainder with registers. -instruct modI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct modI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (ModI src1 src2)); ins_cost(10*DEFAULT_COST); @@ -8340,7 +8282,7 @@ instruct modL_reg_reg_Ex(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ %} %} -instruct udivI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct udivI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (UDivI src1 src2)); format %{ "DIVWU $dst, $src1, $src2" %} size(4); @@ -8350,7 +8292,7 @@ instruct udivI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct umodI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct umodI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (UModI src1 src2)); expand %{ iRegIdst tmp1; @@ -8416,7 +8358,7 @@ instruct lShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct lShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct lShiftI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (LShiftI src1 src2)); ins_cost(DEFAULT_COST*2); expand %{ @@ -8428,7 +8370,7 @@ instruct lShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Register Shift Left Immediate -instruct lShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ +instruct lShiftI_reg_imm(iRegIdst dst, iRegIorL2I src1, immI src2) %{ match(Set dst (LShiftI src1 src2)); format %{ "SLWI $dst, $src1, ($src2 & 0x1f)" %} @@ -8440,7 +8382,7 @@ instruct lShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ %} // AndI with negpow2-constant + LShiftI -instruct lShiftI_andI_immInegpow2_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, uimmI5 src3) %{ +instruct lShiftI_andI_immInegpow2_imm5(iRegIdst dst, iRegIorL2I src1, immInegpow2 src2, uimmI5 src3) %{ match(Set dst (LShiftI (AndI src1 src2) src3)); predicate(UseRotateAndMaskInstructionsPPC64); @@ -8459,7 +8401,7 @@ instruct lShiftI_andI_immInegpow2_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 %} // RShiftI + AndI with negpow2-constant + LShiftI -instruct lShiftI_andI_immInegpow2_rShiftI_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, uimmI5 src3) %{ +instruct lShiftI_andI_immInegpow2_rShiftI_imm5(iRegIdst dst, iRegIorL2I src1, immInegpow2 src2, uimmI5 src3) %{ match(Set dst (LShiftI (AndI (RShiftI src1 src3) src2) src3)); predicate(UseRotateAndMaskInstructionsPPC64); @@ -8491,7 +8433,7 @@ instruct lShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ %} // Register Shift Left -instruct lShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ +instruct lShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIorL2I src2) %{ match(Set dst (LShiftL src1 src2)); ins_cost(DEFAULT_COST*2); expand %{ @@ -8514,7 +8456,7 @@ instruct lshiftL_regL_immI(iRegLdst dst, iRegLsrc src1, immI src2) %{ %} // If we shift more than 32 bits, we need not convert I2L. -instruct lShiftL_regI_immGE32(iRegLdst dst, iRegIsrc src1, uimmI6_ge32 src2) %{ +instruct lShiftL_regI_immGE32(iRegLdst dst, iRegIorL2I src1, uimmI6_ge32 src2) %{ match(Set dst (LShiftL (ConvI2L src1) src2)); ins_cost(DEFAULT_COST); @@ -8528,7 +8470,7 @@ instruct lShiftL_regI_immGE32(iRegLdst dst, iRegIsrc src1, uimmI6_ge32 src2) %{ // Shift a postivie int to the left. // Clrlsldi clears the upper 32 bits and shifts. -instruct scaledPositiveI2L_lShiftL_convI2L_reg_imm6(iRegLdst dst, iRegIsrc src1, uimmI6 src2) %{ +instruct scaledPositiveI2L_lShiftL_convI2L_reg_imm6(iRegLdst dst, iRegIorL2I src1, uimmI6 src2) %{ match(Set dst (LShiftL (ConvI2L src1) src2)); predicate(((ConvI2LNode*)(_kids[0]->_leaf))->type()->is_long()->is_positive_int()); @@ -8554,7 +8496,7 @@ instruct arShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Register Arithmetic Shift Right -instruct arShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct arShiftI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (RShiftI src1 src2)); ins_cost(DEFAULT_COST*2); expand %{ @@ -8566,7 +8508,7 @@ instruct arShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Register Arithmetic Shift Right Immediate -instruct arShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ +instruct arShiftI_reg_imm(iRegIdst dst, iRegIorL2I src1, immI src2) %{ match(Set dst (RShiftI src1 src2)); format %{ "SRAWI $dst, $src1, ($src2 & 0x1f)" %} @@ -8591,7 +8533,7 @@ instruct arShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ %} // Register Shift Right Arithmetic Long -instruct arShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ +instruct arShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIorL2I src2) %{ match(Set dst (RShiftL src1 src2)); ins_cost(DEFAULT_COST*2); @@ -8641,7 +8583,7 @@ instruct urShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Register Shift Right -instruct urShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct urShiftI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (URShiftI src1 src2)); ins_cost(DEFAULT_COST*2); @@ -8654,7 +8596,7 @@ instruct urShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} // Register Shift Right Immediate -instruct urShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ +instruct urShiftI_reg_imm(iRegIdst dst, iRegIorL2I src1, immI src2) %{ match(Set dst (URShiftI src1 src2)); format %{ "SRWI $dst, $src1, ($src2 & 0x1f)" %} @@ -8679,7 +8621,7 @@ instruct urShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ %} // Register Shift Right -instruct urShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ +instruct urShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIorL2I src2) %{ match(Set dst (URShiftL src1 src2)); ins_cost(DEFAULT_COST*2); @@ -8728,7 +8670,7 @@ instruct shrP_convP2X_reg_imm6(iRegLdst dst, iRegP_N2P src1, uimmI6 src2) %{ %} // Bitfield Extract: URShiftI + AndI -instruct andI_urShiftI_regI_immI_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immI src2, immIpow2minus1 src3) %{ +instruct andI_urShiftI_regI_immI_immIpow2minus1(iRegIdst dst, iRegIorL2I src1, immI src2, immIpow2minus1 src3) %{ match(Set dst (AndI (URShiftI src1 src2) src3)); format %{ "EXTRDI $dst, $src1, shift=$src2, mask=$src3 \t// int bitfield extract" %} @@ -8763,21 +8705,10 @@ instruct andL_urShiftL_regL_immI_immLpow2minus1(iRegLdst dst, iRegLsrc src1, imm ins_pipe(pipe_class_default); %} -instruct sxtI_reg(iRegIdst dst, iRegIsrc src) %{ - match(Set dst (ConvL2I (ConvI2L src))); - - format %{ "EXTSW $dst, $src \t// int->int" %} - size(4); - ins_encode %{ - __ extsw($dst$$Register, $src$$Register); - %} - ins_pipe(pipe_class_default); -%} - //----------Rotate Instructions------------------------------------------------ // Rotate Left by 8-bit immediate -instruct rotlI_reg_immi8(iRegIdst dst, iRegIsrc src, immI8 lshift, immI8 rshift) %{ +instruct rotlI_reg_immi8(iRegIdst dst, iRegIorL2I src, immI8 lshift, immI8 rshift) %{ match(Set dst (OrI (LShiftI src lshift) (URShiftI src rshift))); predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); @@ -8790,7 +8721,7 @@ instruct rotlI_reg_immi8(iRegIdst dst, iRegIsrc src, immI8 lshift, immI8 rshift) %} // Rotate Right by 8-bit immediate -instruct rotrI_reg_immi8(iRegIdst dst, iRegIsrc src, immI8 rshift, immI8 lshift) %{ +instruct rotrI_reg_immi8(iRegIdst dst, iRegIorL2I src, immI8 rshift, immI8 lshift) %{ match(Set dst (OrI (URShiftI src rshift) (LShiftI src lshift))); predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); @@ -9099,7 +9030,7 @@ instruct msubD_reg_reg(regD dst, regD src1, regD src2, regD src3) %{ // And Instructions // Register And -instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct andI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (AndI src1 src2)); format %{ "AND $dst, $src1, $src2" %} size(4); @@ -9109,7 +9040,7 @@ instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct andI_reg_immI(iRegIdst dst, iRegIsrc src1, immI src2, flagsRegCR0 cr0) %{ +instruct andI_reg_immI(iRegIdst dst, iRegIorL2I src1, immI src2, flagsRegCR0 cr0) %{ match(Set dst (AndI src1 src2)); predicate(Assembler::andi_supports((juint)(n->in(2)->get_int()))); effect(KILL cr0); @@ -9162,7 +9093,7 @@ instruct convL2I_andL_reg_immL(iRegIdst dst, iRegLsrc src1, immL src2, flagsRegC // Or Instructions // Register Or -instruct orI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct orI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (OrI src1 src2)); format %{ "OR $dst, $src1, $src2" %} size(4); @@ -9184,7 +9115,7 @@ instruct orI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct tree_orI_orI_orI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ +instruct tree_orI_orI_orI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, iRegIorL2I src3, iRegIorL2I src4) %{ match(Set dst (OrI (OrI (OrI src1 src2) src3) src4)); ins_cost(DEFAULT_COST*3); @@ -9199,7 +9130,7 @@ instruct tree_orI_orI_orI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, %} // Immediate Or -instruct orI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ +instruct orI_reg_uimm16(iRegIdst dst, iRegIorL2I src1, uimmI16 src2) %{ match(Set dst (OrI src1 src2)); format %{ "ORI $dst, $src1, $src2" %} size(4); @@ -9251,7 +9182,7 @@ instruct orL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 con) %{ // Xor Instructions // Register Xor -instruct xorI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct xorI_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (XorI src1 src2)); format %{ "XOR $dst, $src1, $src2" %} size(4); @@ -9273,7 +9204,7 @@ instruct xorI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -instruct tree_xorI_xorI_xorI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ +instruct tree_xorI_xorI_xorI_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, iRegIorL2I src3, iRegIorL2I src4) %{ match(Set dst (XorI (XorI (XorI src1 src2) src3) src4)); ins_cost(DEFAULT_COST*3); @@ -9288,7 +9219,7 @@ instruct tree_xorI_xorI_xorI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc sr %} // Immediate Xor -instruct xorI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ +instruct xorI_reg_uimm16(iRegIdst dst, iRegIorL2I src1, uimmI16 src2) %{ match(Set dst (XorI src1 src2)); format %{ "XORI $dst, $src1, $src2" %} size(4); @@ -9337,7 +9268,7 @@ instruct xorL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2) %{ ins_pipe(pipe_class_default); %} -instruct notI_reg(iRegIdst dst, iRegIsrc src1, immI_minus1 src2) %{ +instruct notI_reg(iRegIdst dst, iRegIorL2I src1, immI_minus1 src2) %{ match(Set dst (XorI src1 src2)); ins_cost(DEFAULT_COST); @@ -9362,7 +9293,7 @@ instruct notL_reg(iRegLdst dst, iRegLsrc src1, immL_minus1 src2) %{ %} // And-complement -instruct andcI_reg_reg(iRegIdst dst, iRegIsrc src1, immI_minus1 src2, iRegIsrc src3) %{ +instruct andcI_reg_reg(iRegIdst dst, iRegIorL2I src1, immI_minus1 src2, iRegIorL2I src3) %{ match(Set dst (AndI (XorI src1 src2) src3)); ins_cost(DEFAULT_COST); @@ -9506,7 +9437,7 @@ instruct moveI2F_stack_reg(regF dst, stackSlotI src) %{ %} // Move integer value from integer register to float stack-location. -instruct moveI2F_reg_stack(stackSlotF dst, iRegIsrc src) %{ +instruct moveI2F_reg_stack(stackSlotF dst, iRegIorL2I src) %{ match(Set dst (MoveI2F src)); ins_cost(MEMORY_REF_COST); @@ -9683,7 +9614,7 @@ instruct checkCastPP(iRegPdst dst) %{ // 3) Xori the result to get 0b1 if src != 0 and 0b0 if src == 0. // convI2Bool -instruct convI2Bool_reg__cntlz_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct convI2Bool_reg__cntlz_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (Conv2B src)); predicate(UseCountLeadingZerosInstructionsPPC64); ins_cost(DEFAULT_COST); @@ -9699,7 +9630,7 @@ instruct convI2Bool_reg__cntlz_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct convI2Bool_reg__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx) %{ +instruct convI2Bool_reg__cmove(iRegIdst dst, iRegIorL2I src, flagsReg crx) %{ match(Set dst (Conv2B src)); effect(TEMP crx); predicate(!UseCountLeadingZerosInstructionsPPC64); @@ -9716,7 +9647,7 @@ instruct convI2Bool_reg__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx) %{ %} // ConvI2B + XorI -instruct xorI_convI2Bool_reg_immIvalue1__cntlz_Ex(iRegIdst dst, iRegIsrc src, immI_1 mask) %{ +instruct xorI_convI2Bool_reg_immIvalue1__cntlz_Ex(iRegIdst dst, iRegIorL2I src, immI_1 mask) %{ match(Set dst (XorI (Conv2B src) mask)); predicate(UseCountLeadingZerosInstructionsPPC64); ins_cost(DEFAULT_COST); @@ -9729,7 +9660,7 @@ instruct xorI_convI2Bool_reg_immIvalue1__cntlz_Ex(iRegIdst dst, iRegIsrc src, im %} %} -instruct xorI_convI2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx, immI_1 mask) %{ +instruct xorI_convI2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegIorL2I src, flagsReg crx, immI_1 mask) %{ match(Set dst (XorI (Conv2B src) mask)); effect(TEMP crx); predicate(!UseCountLeadingZerosInstructionsPPC64); @@ -9746,7 +9677,7 @@ instruct xorI_convI2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegIsrc src, flags %} // AndI 0b0..010..0 + ConvI2B -instruct convI2Bool_andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src, immIpowerOf2 mask) %{ +instruct convI2Bool_andI_reg_immIpowerOf2(iRegIdst dst, iRegIorL2I src, immIpowerOf2 mask) %{ match(Set dst (Conv2B (AndI src mask))); predicate(UseRotateAndMaskInstructionsPPC64); ins_cost(DEFAULT_COST); @@ -9834,7 +9765,7 @@ instruct xorI_convP2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegP_N2P src, flag %} // if src1 < src2, return -1 else return 0 -instruct cmpLTMask_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ +instruct cmpLTMask_reg_reg_Ex(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set dst (CmpLTMask src1 src2)); ins_cost(DEFAULT_COST*4); @@ -9850,7 +9781,7 @@ instruct cmpLTMask_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ %} %} -instruct cmpLTMask_reg_immI0(iRegIdst dst, iRegIsrc src1, immI_0 src2) %{ +instruct cmpLTMask_reg_immI0(iRegIdst dst, iRegIorL2I src1, immI_0 src2) %{ match(Set dst (CmpLTMask src1 src2)); // if src1 < src2, return -1 else return 0 format %{ "SRAWI $dst, $src1, $src2 \t// CmpLTMask" %} size(4); @@ -9867,7 +9798,7 @@ instruct cmpLTMask_reg_immI0(iRegIdst dst, iRegIsrc src1, immI_0 src2) %{ // Convert to Int -instruct convB2I_reg(iRegIdst dst, iRegIsrc src, immI_24 amount) %{ +instruct convB2I_reg(iRegIdst dst, iRegIorL2I src, immI_24 amount) %{ match(Set dst (RShiftI (LShiftI src amount) amount)); format %{ "EXTSB $dst, $src \t// byte->int" %} size(4); @@ -9888,7 +9819,7 @@ instruct extsh(iRegIdst dst, iRegIsrc src) %{ %} // LShiftI 16 + RShiftI 16 converts short to int. -instruct convS2I_reg(iRegIdst dst, iRegIsrc src, immI_16 amount) %{ +instruct convS2I_reg(iRegIdst dst, iRegIorL2I src, immI_16 amount) %{ match(Set dst (RShiftI (LShiftI src amount) amount)); format %{ "EXTSH $dst, $src \t// short->int" %} size(4); @@ -9898,18 +9829,6 @@ instruct convS2I_reg(iRegIdst dst, iRegIsrc src, immI_16 amount) %{ ins_pipe(pipe_class_default); %} -// ConvL2I + ConvI2L: Sign extend int in long register. -instruct sxtI_L2L_reg(iRegLdst dst, iRegLsrc src) %{ - match(Set dst (ConvI2L (ConvL2I src))); - - format %{ "EXTSW $dst, $src \t// long->long" %} - size(4); - ins_encode %{ - __ extsw($dst$$Register, $src$$Register); - %} - ins_pipe(pipe_class_default); -%} - instruct convL2I_reg(iRegIdst dst, iRegLsrc src) %{ match(Set dst (ConvL2I src)); format %{ "MR $dst, $src \t// long->int" %} @@ -10005,7 +9924,7 @@ instruct convF2I_regF_mffprd_ExEx(iRegIdst dst, regF src) %{ // Convert to Long -instruct convI2L_reg(iRegLdst dst, iRegIsrc src) %{ +instruct convI2L_reg(iRegLdst dst, iRegIorL2I src) %{ match(Set dst (ConvI2L src)); format %{ "EXTSW $dst, $src \t// int->long" %} size(4); @@ -10016,7 +9935,7 @@ instruct convI2L_reg(iRegLdst dst, iRegIsrc src) %{ %} // Zero-extend: convert unsigned int to long (convUI2L). -instruct zeroExtendL_regI(iRegLdst dst, iRegIsrc src, immL_32bits mask) %{ +instruct zeroExtendL_regI(iRegLdst dst, iRegIorL2I src, immL_32bits mask) %{ match(Set dst (AndL (ConvI2L src) mask)); ins_cost(DEFAULT_COST); @@ -10165,7 +10084,7 @@ instruct convL2FRaw_regF(regF dst, regD src) %{ // Integer to Float conversion. Special version for Power8. -instruct convI2F_ireg_mtfprd_Ex(regF dst, iRegIsrc src) %{ +instruct convI2F_ireg_mtfprd_Ex(regF dst, iRegIorL2I src) %{ match(Set dst (ConvI2F src)); ins_cost(DEFAULT_COST); @@ -10196,7 +10115,7 @@ instruct convL2F_ireg_mtfprd_Ex(regF dst, iRegLsrc src) %{ // Integer to Double conversion. Special version for Power8. -instruct convI2D_reg_mtfprd_Ex(regD dst, iRegIsrc src) %{ +instruct convI2D_reg_mtfprd_Ex(regD dst, iRegIorL2I src) %{ match(Set dst (ConvI2D src)); ins_cost(DEFAULT_COST); @@ -10245,7 +10164,7 @@ instruct convF2HF_reg_reg(iRegIdst dst, regF src, regF tmp) %{ ins_pipe(pipe_class_default); %} -instruct convHF2F_reg_reg(regF dst, iRegIsrc src) %{ +instruct convHF2F_reg_reg(regF dst, iRegIorL2I src) %{ match(Set dst (ConvHF2F src)); ins_cost(2 * DEFAULT_COST); size(8); @@ -10262,7 +10181,7 @@ instruct convHF2F_reg_reg(regF dst, iRegIsrc src) %{ // Compare Instructions // Compare Integers -instruct cmpI_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ +instruct cmpI_reg_reg(flagsReg crx, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set crx (CmpI src1 src2)); size(4); format %{ "CMPW $crx, $src1, $src2" %} @@ -10272,7 +10191,7 @@ instruct cmpI_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_compare); %} -instruct cmpI_reg_imm16(flagsReg crx, iRegIsrc src1, immI16 src2) %{ +instruct cmpI_reg_imm16(flagsReg crx, iRegIorL2I src1, immI16 src2) %{ match(Set crx (CmpI src1 src2)); format %{ "CMPWI $crx, $src1, $src2" %} size(4); @@ -10283,7 +10202,7 @@ instruct cmpI_reg_imm16(flagsReg crx, iRegIsrc src1, immI16 src2) %{ %} // (src1 & src2) == 0? -instruct testI_reg_imm(flagsRegCR0 cr0, iRegIsrc src1, uimmI16 src2, immI_0 zero) %{ +instruct testI_reg_imm(flagsRegCR0 cr0, iRegIorL2I src1, uimmI16 src2, immI_0 zero) %{ match(Set cr0 (CmpI (AndI src1 src2) zero)); // r0 is killed format %{ "ANDI R0, $src1, $src2 \t// BTST int" %} @@ -10373,7 +10292,7 @@ instruct cmpL3_reg_reg(iRegIdst dst, iRegLsrc src1, iRegLsrc src2, flagsRegCR0 c ins_pipe(pipe_class_default); %} -instruct cmpU3_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct cmpU3_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set dst (CmpU3 src1 src2)); effect(KILL cr0); ins_cost(DEFAULT_COST * 5); @@ -10409,7 +10328,7 @@ instruct cmpUL3_reg_reg(iRegIdst dst, iRegLsrc src1, iRegLsrc src2, flagsRegCR0 // - (If lt (CmpU index length)), (IfFalse throw exception) // // Match range check 'If le (CmpU length index)'. -instruct rangeCheck_iReg_uimm15(cmpOp cmp, iRegIsrc src_length, uimmI15 index, label labl) %{ +instruct rangeCheck_iReg_uimm15(cmpOp cmp, iRegIorL2I src_length, uimmI15 index, label labl) %{ match(If cmp (CmpU src_length index)); effect(USE labl); predicate(TrapBasedRangeChecks && @@ -10435,7 +10354,7 @@ instruct rangeCheck_iReg_uimm15(cmpOp cmp, iRegIsrc src_length, uimmI15 index, l %} // Match range check 'If lt (CmpU index length)'. -instruct rangeCheck_iReg_iReg(cmpOp cmp, iRegIsrc src_index, iRegIsrc src_length, label labl) %{ +instruct rangeCheck_iReg_iReg(cmpOp cmp, iRegIorL2I src_index, iRegIorL2I src_length, label labl) %{ match(If cmp (CmpU src_index src_length)); effect(USE labl); predicate(TrapBasedRangeChecks && @@ -10461,7 +10380,7 @@ instruct rangeCheck_iReg_iReg(cmpOp cmp, iRegIsrc src_index, iRegIsrc src_length %} // Match range check 'If lt (CmpU index length)'. -instruct rangeCheck_uimm15_iReg(cmpOp cmp, iRegIsrc src_index, uimmI15 length, label labl) %{ +instruct rangeCheck_uimm15_iReg(cmpOp cmp, iRegIorL2I src_index, uimmI15 length, label labl) %{ match(If cmp (CmpU src_index length)); effect(USE labl); predicate(TrapBasedRangeChecks && @@ -10486,7 +10405,7 @@ instruct rangeCheck_uimm15_iReg(cmpOp cmp, iRegIsrc src_index, uimmI15 length, l ins_pipe(pipe_class_trap); %} -instruct compU_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ +instruct compU_reg_reg(flagsReg crx, iRegIorL2I src1, iRegIorL2I src2) %{ match(Set crx (CmpU src1 src2)); format %{ "CMPLW $crx, $src1, $src2 \t// unsigned" %} size(4); @@ -10496,7 +10415,7 @@ instruct compU_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_compare); %} -instruct compU_reg_uimm16(flagsReg crx, iRegIsrc src1, uimmI16 src2) %{ +instruct compU_reg_uimm16(flagsReg crx, iRegIorL2I src1, uimmI16 src2) %{ match(Set crx (CmpU src1 src2)); size(4); format %{ "CMPLWI $crx, $src1, $src2" %} @@ -10728,7 +10647,7 @@ instruct cmpD3_reg_reg(iRegIdst dst, regD src1, regD src2, flagsRegCR0 cr0) %{ %} // Compare char -instruct cmprb_Digit_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{ +instruct cmprb_Digit_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIsrc src2, flagsReg crx) %{ match(Set dst (Digit src1)); effect(TEMP src2, TEMP crx); ins_cost(3 * DEFAULT_COST); @@ -10747,7 +10666,7 @@ instruct cmprb_Digit_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRe ins_pipe(pipe_class_default); %} -instruct cmprb_LowerCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{ +instruct cmprb_LowerCase_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIsrc src2, flagsReg crx) %{ match(Set dst (LowerCase src1)); effect(TEMP src2, TEMP crx); ins_cost(12 * DEFAULT_COST); @@ -10797,7 +10716,7 @@ instruct cmprb_LowerCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, fla ins_pipe(pipe_class_default); %} -instruct cmprb_UpperCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{ +instruct cmprb_UpperCase_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIsrc src2, flagsReg crx) %{ match(Set dst (UpperCase src1)); effect(TEMP src2, TEMP crx); ins_cost(7 * DEFAULT_COST); @@ -10832,7 +10751,7 @@ instruct cmprb_UpperCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, fla ins_pipe(pipe_class_default); %} -instruct cmprb_Whitespace_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{ +instruct cmprb_Whitespace_reg_reg(iRegIdst dst, iRegIorL2I src1, iRegIsrc src2, flagsReg crx) %{ match(Set dst (Whitespace src1)); predicate(PowerArchitecturePPC64 <= 9); effect(TEMP src2, TEMP crx); @@ -10855,7 +10774,7 @@ instruct cmprb_Whitespace_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, fl %} // Power 10 version, using prefixed addi to load 32-bit constant -instruct cmprb_Whitespace_reg_reg_prefixed(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{ +instruct cmprb_Whitespace_reg_reg_prefixed(iRegIdst dst, iRegIorL2I src1, iRegIsrc src2, flagsReg crx) %{ match(Set dst (Whitespace src1)); predicate(PowerArchitecturePPC64 >= 10); effect(TEMP src2, TEMP crx); @@ -11265,7 +11184,7 @@ instruct array_equalsC(rarg1RegP ary1, rarg2RegP ary2, iRegIdst result, ins_pipe(pipe_class_default); %} -instruct indexOf_imm1_char_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_char_U(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, immP needleImm, immL offsetImm, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11298,7 +11217,7 @@ instruct indexOf_imm1_char_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt ins_pipe(pipe_class_compare); %} -instruct indexOf_imm1_char_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_char_L(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, immP needleImm, immL offsetImm, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11324,7 +11243,7 @@ instruct indexOf_imm1_char_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt ins_pipe(pipe_class_compare); %} -instruct indexOf_imm1_char_UL(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_char_UL(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, immP needleImm, immL offsetImm, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11350,7 +11269,7 @@ instruct indexOf_imm1_char_UL(iRegIdst result, iRegPsrc haystack, iRegIsrc haycn ins_pipe(pipe_class_compare); %} -instruct indexOf_imm1_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_U(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, rscratch2RegP needle, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11384,7 +11303,7 @@ instruct indexOf_imm1_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, ins_pipe(pipe_class_compare); %} -instruct indexOf_imm1_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_L(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, rscratch2RegP needle, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11411,7 +11330,7 @@ instruct indexOf_imm1_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, ins_pipe(pipe_class_compare); %} -instruct indexOf_imm1_UL(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, +instruct indexOf_imm1_UL(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, rscratch2RegP needle, immI_1 needlecntImm, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ @@ -11438,8 +11357,8 @@ instruct indexOf_imm1_UL(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, ins_pipe(pipe_class_compare); %} -instruct indexOfChar_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, - iRegIsrc ch, iRegIdst tmp1, iRegIdst tmp2, +instruct indexOfChar_U(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, + iRegIorL2I ch, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); effect(TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); @@ -11457,8 +11376,8 @@ instruct indexOfChar_U(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, ins_pipe(pipe_class_compare); %} -instruct indexOfChar_L(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, - iRegIsrc ch, iRegIdst tmp1, iRegIdst tmp2, +instruct indexOfChar_L(iRegIdst result, iRegPsrc haystack, iRegIorL2I haycnt, + iRegIorL2I ch, iRegIdst tmp1, iRegIdst tmp2, flagsRegCR0 cr0, flagsRegCR1 cr1, regCTR ctr) %{ match(Set result (StrIndexOfChar (Binary haystack haycnt) ch)); effect(TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1, KILL ctr); @@ -11621,7 +11540,7 @@ instruct indexOf_UL(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, iR %} // char[] to byte[] compression -instruct string_compress(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, +instruct string_compress(rarg1RegP src, rarg2RegP dst, iRegIorL2I len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ match(Set result (StrCompressedCopy src (Binary dst len))); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, @@ -11636,7 +11555,7 @@ instruct string_compress(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst re %} // byte[] to char[] inflation -instruct string_inflate(Universe dummy, rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegLdst tmp1, +instruct string_inflate(Universe dummy, rarg1RegP src, rarg2RegP dst, iRegIorL2I len, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ match(Set dummy (StrInflatedCopy src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); @@ -11655,7 +11574,7 @@ instruct string_inflate(Universe dummy, rarg1RegP src, rarg2RegP dst, iRegIsrc l %} // StringCoding.java intrinsics -instruct count_positives(iRegPsrc ary1, iRegIsrc len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, +instruct count_positives(iRegPsrc ary1, iRegIorL2I len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, regCTR ctr, flagsRegCR0 cr0) %{ match(Set result (CountPositives ary1 len)); @@ -11670,7 +11589,7 @@ instruct count_positives(iRegPsrc ary1, iRegIsrc len, iRegIdst result, iRegLdst %} // encode char[] to byte[] in ISO_8859_1 -instruct encode_iso_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, +instruct encode_iso_array(rarg1RegP src, rarg2RegP dst, iRegIorL2I len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); @@ -11686,7 +11605,7 @@ instruct encode_iso_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst r %} // encode char[] to byte[] in ASCII -instruct encode_ascii_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, +instruct encode_ascii_array(rarg1RegP src, rarg2RegP dst, iRegIorL2I len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ predicate(((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); @@ -11705,7 +11624,7 @@ instruct encode_ascii_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst //---------- Min/Max Instructions --------------------------------------------- -instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct minI_reg_reg_isel(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set dst (MinI src1 src2)); effect(KILL cr0); ins_cost(DEFAULT_COST*2); @@ -11719,7 +11638,7 @@ instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC %} -instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegCR0 cr0) %{ +instruct maxI_reg_reg_isel(iRegIdst dst, iRegIorL2I src1, iRegIorL2I src2, flagsRegCR0 cr0) %{ match(Set dst (MaxI src1 src2)); effect(KILL cr0); ins_cost(DEFAULT_COST*2); @@ -11786,7 +11705,7 @@ instruct maxD(regD dst, regD src1, regD src2) %{ //---------- Population Count Instructions ------------------------------------ -instruct popCountI(iRegIdst dst, iRegIsrc src) %{ +instruct popCountI(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (PopCountI src)); predicate(UsePopCountInstruction); ins_cost(DEFAULT_COST); @@ -11812,7 +11731,7 @@ instruct popCountL(iRegIdst dst, iRegLsrc src) %{ ins_pipe(pipe_class_default); %} -instruct countLeadingZerosI(iRegIdst dst, iRegIsrc src) %{ +instruct countLeadingZerosI(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (CountLeadingZerosI src)); predicate(UseCountLeadingZerosInstructionsPPC64); // See Matcher::match_rule_supported. ins_cost(DEFAULT_COST); @@ -11851,7 +11770,7 @@ instruct countLeadingZerosP(iRegIdst dst, iRegPsrc src) %{ ins_pipe(pipe_class_default); %} -instruct countTrailingZerosI_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct countTrailingZerosI_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (CountTrailingZerosI src)); predicate(UseCountLeadingZerosInstructionsPPC64 && !UseCountTrailingZerosInstructionsPPC64); ins_cost(DEFAULT_COST); @@ -11870,7 +11789,7 @@ instruct countTrailingZerosI_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct countTrailingZerosI_cnttzw(iRegIdst dst, iRegIsrc src) %{ +instruct countTrailingZerosI_cnttzw(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (CountTrailingZerosI src)); predicate(UseCountTrailingZerosInstructionsPPC64); ins_cost(DEFAULT_COST); @@ -11954,7 +11873,7 @@ instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 n, immI16 b) %{ %} // Just slightly faster than java implementation. -instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesI src)); predicate(!UseByteReverseInstructions); ins_cost(7*DEFAULT_COST); @@ -11979,7 +11898,7 @@ instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct bytes_reverse_int_vec(iRegIdst dst, iRegIsrc src, vecX tmpV) %{ +instruct bytes_reverse_int_vec(iRegIdst dst, iRegIorL2I src, vecX tmpV) %{ match(Set dst (ReverseBytesI src)); predicate(UseVectorByteReverseInstructionsPPC64); effect(TEMP tmpV); @@ -11997,7 +11916,7 @@ instruct bytes_reverse_int_vec(iRegIdst dst, iRegIsrc src, vecX tmpV) %{ ins_pipe(pipe_class_default); %} -instruct bytes_reverse_int(iRegIdst dst, iRegIsrc src) %{ +instruct bytes_reverse_int(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesI src)); predicate(UseByteReverseInstructions); ins_cost(DEFAULT_COST); @@ -12084,7 +12003,7 @@ instruct bytes_reverse_long(iRegLdst dst, iRegLsrc src) %{ %} // Need zero extend. Must not use brh only. -instruct bytes_reverse_ushort_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct bytes_reverse_ushort_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesUS src)); ins_cost(2*DEFAULT_COST); @@ -12099,7 +12018,7 @@ instruct bytes_reverse_ushort_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct bytes_reverse_short_Ex(iRegIdst dst, iRegIsrc src) %{ +instruct bytes_reverse_short_Ex(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesS src)); predicate(!UseByteReverseInstructions); ins_cost(3*DEFAULT_COST); @@ -12115,7 +12034,7 @@ instruct bytes_reverse_short_Ex(iRegIdst dst, iRegIsrc src) %{ %} %} -instruct bytes_reverse_short(iRegIdst dst, iRegIsrc src) %{ +instruct bytes_reverse_short(iRegIdst dst, iRegIorL2I src) %{ match(Set dst (ReverseBytesS src)); predicate(UseByteReverseInstructions); ins_cost(DEFAULT_COST); @@ -12238,7 +12157,7 @@ instruct loadS_reversed_acquire(iRegIdst dst, indirect mem) %{ %} // Store Integer reversed byte order -instruct storeI_reversed(iRegIsrc src, indirect mem) %{ +instruct storeI_reversed(iRegIorL2I src, indirect mem) %{ match(Set mem (StoreI mem (ReverseBytesI src))); ins_cost(MEMORY_REF_COST); @@ -12262,7 +12181,7 @@ instruct storeL_reversed(iRegLsrc src, indirect mem) %{ %} // Store unsigned short / char reversed byte order -instruct storeUS_reversed(iRegIsrc src, indirect mem) %{ +instruct storeUS_reversed(iRegIorL2I src, indirect mem) %{ match(Set mem (StoreC mem (ReverseBytesUS src))); ins_cost(MEMORY_REF_COST); @@ -12274,7 +12193,7 @@ instruct storeUS_reversed(iRegIsrc src, indirect mem) %{ %} // Store short reversed byte order -instruct storeS_reversed(iRegIsrc src, indirect mem) %{ +instruct storeS_reversed(iRegIorL2I src, indirect mem) %{ match(Set mem (StoreC mem (ReverseBytesS src))); ins_cost(MEMORY_REF_COST); @@ -12359,7 +12278,7 @@ instruct repl56(iRegLdst dst) %{ ins_pipe(pipe_class_default); %} -instruct repl8B_reg_Ex(iRegLdst dst, iRegIsrc src) %{ +instruct repl8B_reg_Ex(iRegLdst dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 8 && Matcher::vector_element_basic_type(n) == T_BYTE); @@ -12395,7 +12314,7 @@ instruct repl8B_immIminus1(iRegLdst dst, immI_minus1 src) %{ ins_pipe(pipe_class_default); %} -instruct repl16B_reg_Ex(vecX dst, iRegIsrc src) %{ +instruct repl16B_reg_Ex(vecX dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 16 && Matcher::vector_element_basic_type(n) == T_BYTE); @@ -12438,7 +12357,7 @@ instruct repl16B_immIminus1(vecX dst, immI_minus1 src) %{ ins_pipe(pipe_class_default); %} -instruct repl4S_reg_Ex(iRegLdst dst, iRegIsrc src) %{ +instruct repl4S_reg_Ex(iRegLdst dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 4 && Matcher::vector_element_basic_type(n) == T_SHORT); @@ -12473,7 +12392,7 @@ instruct repl4S_immIminus1(iRegLdst dst, immI_minus1 src) %{ ins_pipe(pipe_class_default); %} -instruct repl8S_reg_Ex(vecX dst, iRegIsrc src) %{ +instruct repl8S_reg_Ex(vecX dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 8 && Matcher::vector_element_basic_type(n) == T_SHORT); @@ -12516,7 +12435,7 @@ instruct repl8S_immIminus1(vecX dst, immI_minus1 src) %{ ins_pipe(pipe_class_default); %} -instruct repl2I_reg_Ex(iRegLdst dst, iRegIsrc src) %{ +instruct repl2I_reg_Ex(iRegLdst dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 2 && Matcher::vector_element_basic_type(n) == T_INT); @@ -12551,7 +12470,7 @@ instruct repl2I_immIminus1(iRegLdst dst, immI_minus1 src) %{ ins_pipe(pipe_class_default); %} -instruct repl4I_reg_Ex(vecX dst, iRegIsrc src) %{ +instruct repl4I_reg_Ex(vecX dst, iRegIorL2I src) %{ match(Set dst (Replicate src)); predicate(n->as_Vector()->length() == 4 && Matcher::vector_element_basic_type(n) == T_INT); @@ -12959,7 +12878,7 @@ instruct vxor(vecX dst, vecX src1, vecX src2) %{ ins_pipe(pipe_class_default); %} -instruct reductionI_arith_logic(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX tmp1, vecX tmp2) %{ +instruct reductionI_arith_logic(iRegIdst dst, iRegIorL2I srcInt, vecX srcVec, vecX tmp1, vecX tmp2) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (AddReductionVI srcInt srcVec)); match(Set dst (MulReductionVI srcInt srcVec)); @@ -12978,7 +12897,7 @@ instruct reductionI_arith_logic(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX ins_pipe(pipe_class_default); %} -instruct reductionI_min_max(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX tmp1, vecX tmp2, flagsRegCR0 cr0) %{ +instruct reductionI_min_max(iRegIdst dst, iRegIorL2I srcInt, vecX srcVec, vecX tmp1, vecX tmp2, flagsRegCR0 cr0) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (MinReductionV srcInt srcVec)); match(Set dst (MaxReductionV srcInt srcVec)); @@ -13957,23 +13876,6 @@ instruct cacheWBPostSync() // match(Set dst (LoadI mem)); // %} // -peephole %{ - peepmatch ( loadI storeI ); - peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); - peepreplace ( storeI( 1.mem 1.mem 1.src ) ); -%} - -peephole %{ - peepmatch ( loadL storeL ); - peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); - peepreplace ( storeL( 1.mem 1.mem 1.src ) ); -%} - -peephole %{ - peepmatch ( loadP storeP ); - peepconstraint ( 1.src == 0.dst, 1.dst == 0.mem ); - peepreplace ( storeP( 1.dst 1.dst 1.src ) ); -%} //----------SMARTSPILL RULES--------------------------------------------------- // These must follow all instruction definitions as they use the names diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 12e4d9994a1d..ad089e11d1b1 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -660,8 +660,8 @@ const VMReg java_farg_reg[13] = { const int num_java_iarg_registers = sizeof(java_iarg_reg) / sizeof(java_iarg_reg[0]); const int num_java_farg_registers = sizeof(java_farg_reg) / sizeof(java_farg_reg[0]); -STATIC_ASSERT(num_java_iarg_registers == Argument::n_int_register_parameters_j); -STATIC_ASSERT(num_java_farg_registers == Argument::n_float_register_parameters_j); +static_assert(num_java_iarg_registers == Argument::n_int_register_parameters_j); +static_assert(num_java_farg_registers == Argument::n_float_register_parameters_j); int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, @@ -1073,7 +1073,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, OopMapSet* oop_maps, int& frame_complete, int& frame_size_in_words, - bool alloc_inline_receiver) { + bool alloc_value_receiver) { if (requires_clinit_barrier) { assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; @@ -1406,7 +1406,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, __ unimplemented("C2I_Value_RO"); #if 0 gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); #endif skip_fixup.reset(); } @@ -1415,7 +1415,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I] = __ pc(); entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ true); // Non-scalarized c2i adapter if (regs != regs_cc) { @@ -1430,7 +1430,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, __ unimplemented("C2I_Value2"); #if 0 gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); #endif } // The c2i adapters might safepoint and trigger a GC. The caller must make sure that diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 8048878aef85..351d177bb1f9 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -97,7 +97,7 @@ class StubGenerator: public StubCodeGenerator { int save_nonvolatile_registers_size = __ save_nonvolatile_registers_size(true, SuperwordUseVSX); // some sanity checks - STATIC_ASSERT(StackAlignmentInBytes == 16); + static_assert(StackAlignmentInBytes == 16); assert((sizeof(frame::native_abi_minframe) % 16) == 0, "unaligned"); assert((sizeof(frame::native_abi_reg_args) % 16) == 0, "unaligned"); assert((save_nonvolatile_registers_size % 16) == 0, "unaligned"); @@ -132,7 +132,7 @@ class StubGenerator: public StubCodeGenerator { __ mr(r_entryframe_fp, R1_SP); // calculate frame size - STATIC_ASSERT(Interpreter::logStackElementSize == 3); + static_assert(Interpreter::logStackElementSize == 3); // space for arguments aligned up: ((arg_count + 1) * 8) &~ 15 __ addi(r_frame_size, r_arg_argument_count, 1); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 9a8f09f6bfc6..c0b634c8186d 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3214,7 +3214,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr __ bind(is_nullable); do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, IN_HEAP); } else { - Label null_free_reference, is_flat, rewrite_inline; + Label null_free_reference, is_flat, rewrite_value; __ test_field_is_flat(Rflags, is_flat); __ test_field_is_null_free_value_type(Rflags, null_free_reference); pop_and_check_object(Rclass_or_obj); @@ -3234,12 +3234,12 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr pop_and_check_object(Rclass_or_obj); // Store into the field do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, IN_HEAP); - __ b(rewrite_inline); + __ b(rewrite_value); __ bind(is_flat); pop_and_check_object(Rclass_or_obj); __ write_flat_field(Rcache, Rscratch, Rscratch2, Rclass_or_obj, Roffset, R17_tos); - __ bind(rewrite_inline); + __ bind(rewrite_value); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_vputfield, Rbc, Rscratch, true, byte_no); } diff --git a/src/hotspot/cpu/ppc/vtableStubs_ppc_64.cpp b/src/hotspot/cpu/ppc/vtableStubs_ppc_64.cpp index b34f60cdec95..38ab45623cdd 100644 --- a/src/hotspot/cpu/ppc/vtableStubs_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/vtableStubs_ppc_64.cpp @@ -78,7 +78,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); + assert(SharedRuntime::name_for_receiver() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); const Register rcvr_klass = R11_scratch1; address npe_addr = __ pc(); // npe = null pointer exception @@ -164,7 +164,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); + assert(SharedRuntime::name_for_receiver() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); // Entry arguments: // R19_method: Interface diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index d53c39b2a6ab..b1e8020f3fc6 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -243,8 +243,8 @@ class Address { // Verify the value is trivially destructible regardless of mode, so our // destructor can also be trivial, and so our assignment operator doesn't // need to destruct the old value before copying over it. - static_assert(std::is_trivially_destructible::value, "must be"); - static_assert(std::is_trivially_destructible::value, "must be"); + static_assert(std::is_trivially_destructible::value); + static_assert(std::is_trivially_destructible::value); Address& operator=(const Address& a) { _mode = a._mode; diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index f42f842a9665..daf637aac621 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -791,6 +791,11 @@ void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { } void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide) { + mem2reg(src, dest, type, patch_code, info, wide, /* is_volatile */ false); +} + +void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, + CodeEmitInfo* info, bool wide, bool is_volatile) { assert(src->is_address(), "should not call otherwise"); assert(dest->is_register(), "should not call otherwise"); @@ -806,11 +811,27 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch return; } + if (is_volatile) { + assert(!wide, "wide volatile loads are unsupported"); + load_volatile(from_addr, dest, type, info); + } else { + load_unordered(from_addr, dest, type, wide, info); + } + + if (is_reference_type(type)) { + if (UseCompressedOops && !wide) { + __ decode_heap_oop(dest->as_register()); + } + + __ verify_oop(dest->as_register()); + } +} + +void LIR_Assembler::load_unordered(LIR_Address* from_addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info) { if (info != nullptr) { add_debug_info_for_null_check_here(info); } - int null_check_here = code_offset(); switch (type) { case T_FLOAT: __ flw(dest->as_float_reg(), as_Address(from_addr)); @@ -858,14 +879,11 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch default: ShouldNotReachHere(); } +} - if (is_reference_type(type)) { - if (UseCompressedOops && !wide) { - __ decode_heap_oop(dest->as_register()); - } - - __ verify_oop(dest->as_register()); - } +void LIR_Assembler::load_volatile(LIR_Address* from_addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { + load_unordered(from_addr, dest, type, /* wide */ false, info); + membar_acquire(); } void LIR_Assembler::move(LIR_Opr src, LIR_Opr dst) { @@ -1965,7 +1983,9 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, const LIR_OprList* arg } void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { - if (dest->is_address() || src->is_address()) { + if (src->is_address()) { + mem2reg(src, dest, type, lir_patch_none, info, /* wide */ false, /* is_volatile */ true); + } else if (dest->is_address()) { move_op(src, dest, type, lir_patch_none, info, /* wide */ false); } else { ShouldNotReachHere(); diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp index fb39211bca84..264c8ca40e8a 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp @@ -43,6 +43,11 @@ friend class ArrayCopyStub; Address as_Address(LIR_Address* addr, Register tmp); + void mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, + CodeEmitInfo* info, bool wide, bool is_volatile); + void load_unordered(LIR_Address* from_addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info); + void load_volatile(LIR_Address* from_addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info); + // helper functions which checks for overflow and sets bailout if it // occurs. Always returns a valid embeddable pointer but in the // bailout case the pointer won't be to unique storage. diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index 7d872db8a5a2..23c9f3e610de 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -1259,5 +1259,4 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info) { __ volatile_load_mem_reg(address, result, info); - __ membar_acquire(); } diff --git a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp index 8aa2f902f4a7..cf4dd15fab5b 100644 --- a/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_Runtime1_riscv.cpp @@ -774,7 +774,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { case StubId::c1_new_null_free_array_id: __ mv(t0, (int)Klass::_lh_array_tag_flat_value); // the array can be a flat array. __ beq(t0, tmp, ok); - __ mv(t0, (int)Klass::_lh_array_tag_ref_value); // the array cannot be a flat array (due to the InlineArrayElementMaxFlatSize, etc.) + __ mv(t0, (int)Klass::_lh_array_tag_ref_value); // the array cannot be a flat array. __ beq(t0, tmp, ok); __ stop("assert(is an object or value type array klass)"); break; diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index c52fcf011b6f..a6339a7929e9 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -203,7 +203,7 @@ void BarrierSetAssembler::copy_store_at(MacroAssembler* masm, void BarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath) { // If mask changes we need to ensure that the inverse is still encodable as an immediate - STATIC_ASSERT(JNIHandles::tag_mask == 3); + static_assert(JNIHandles::tag_mask == 3); __ andi(obj, obj, ~JNIHandles::tag_mask); __ ld(obj, Address(obj, 0)); // *obj } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 03841fa48cb5..4d4389f5873c 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -31,10 +31,13 @@ #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" +#include "nativeInst_riscv.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #ifdef COMPILER1 @@ -513,6 +516,45 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #undef __ +address ShenandoahBarrierSetAssembler::parse_jump_address(address pc) { + NativeInstruction* ni = nativeInstruction_at(pc); + assert(ni->is_jump(), "Must be a jump"); + NativeJump* jmp = nativeJump_at(pc); + return jmp->jump_destination(); +} + +static uint32_t encode_patchable_nop() { + return 0x00000013; +} + +static uint32_t encode_patchable_jump(address pc, address target_pc) { + int32_t disp = checked_cast((intptr_t)target_pc - (intptr_t)pc); + return Assembler::encode_jal(x0, disp); +} + +void ShenandoahBarrierSetAssembler::insert_patchable_nop(address pc) { + Assembler::sd_instr(pc, encode_patchable_nop()); + assert(nativeInstruction_at(pc)->is_nop(), "Sanity"); + if (!UseCtxFencei) { + ICache::invalidate_word(pc); + } +} + +void ShenandoahBarrierSetAssembler::insert_patchable_jump(address pc, address target_pc) { + Assembler::sd_instr(pc, encode_patchable_jump(pc, target_pc)); + if (!UseCtxFencei) { + ICache::invalidate_word(pc); + } +} + +bool ShenandoahBarrierSetAssembler::is_patchable_nop(address pc) { + return Assembler::ld_instr(pc) == encode_patchable_nop(); +} + +bool ShenandoahBarrierSetAssembler::is_patchable_jump(address pc, address target_pc) { + return Assembler::ld_instr(pc) == encode_patchable_jump(pc, target_pc); +} + #ifdef COMPILER1 #define __ ce->masm()-> @@ -694,16 +736,25 @@ void ShenandoahBarrierStubC2::cardtable(MacroAssembler& masm, Address address, R } } -void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) { - Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); +void ShenandoahBarrierStubC2::patchable_jump(MacroAssembler& masm, const char gc_state, bool jump_when_state, Register tmp1, Register tmp2, Label* L_target) { + PhaseOutput* const output = Compile::current()->output(); + if (output->in_scratch_emit_size()) { + // Avoid binding L_target in scratch emits. + // We know the patched check is exactly one incompressible instruction long. + Assembler::IncompressibleScope scope(&masm); + __ nop(); + return; + } - Address gc_state_fast(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state))); - __ lbu(tmp, gc_state_fast); - __ beqz(tmp, *continuation()); - __ j(*entry()); + // Emit the unconditional branch in the first version of the method. + // Let the rest of runtime figure out how to manage it. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ j(*L_target); +} - // This is were the slowpath stub will return to or the code above will - // jump to if the checks are false +void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp1, Register tmp2) { + Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); + patchable_jump_if_gc_state(masm, test_state, tmp1, tmp2, entry()); __ bind(*continuation()); } @@ -758,12 +809,11 @@ void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { Address buffer(xthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); Label L_through, L_slowpath; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_load_ref_barrier) { - assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true"); - Address gc_state_fast(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING))); - __ lbu(_tmp1, gc_state_fast); - __ beqz(_tmp1, L_through); + assert(L_done == nullptr, "Should be"); + char state_to_check = ShenandoahHeap::MARKING; + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_through); } // Fast-path: put object into buffer. @@ -812,20 +862,17 @@ void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { Label L_slow; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_keep_alive_barrier) { char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0); - Address gc_state_fast(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check))); - __ lbu(_tmp1, gc_state_fast); - maybe_far_jump_if_zero(masm, _tmp1); + patchable_jump_if_not_gc_state(masm, state_to_check, _tmp1, _tmp2, continuation()); } // If weak references are being processed, weak/phantom loads need to go slow, // regardless of their cset status. if (_needs_load_ref_weak_barrier) { - Address gc_state_fast(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS))); - __ lbu(_tmp1, gc_state_fast); - __ bnez(_tmp1, L_slow); + char state_to_check = ShenandoahHeap::WEAK_ROOTS; + patchable_jump_if_gc_state(masm, state_to_check, _tmp1, _tmp2, &L_slow); } // Cset-check. Fall-through to slow if in collection set. diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index cc2510e7086c..b2aa111a0833 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -81,6 +81,13 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); + // Jumps hotpatching + static address parse_jump_address(address pc); + static void insert_patchable_nop(address pc); + static bool is_patchable_nop(address pc); + static void insert_patchable_jump(address pc, address target_pc); + static bool is_patchable_jump(address pc, address target_pc); + #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index e778dde7d43c..5af1965a3cac 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -845,7 +845,7 @@ void MacroAssembler::resolve_jobject(Register value, Register tmp1, Register tmp bind(tagged); // Test for jweak tag. - STATIC_ASSERT(JNIHandles::TypeTag::weak_global == 0b1); + static_assert(JNIHandles::TypeTag::weak_global == 0b1); test_bit(tmp1, value, exact_log2(JNIHandles::TypeTag::weak_global)); bnez(tmp1, weak_tagged); @@ -872,7 +872,7 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis #ifdef ASSERT { - STATIC_ASSERT(JNIHandles::TypeTag::global == 0b10); + static_assert(JNIHandles::TypeTag::global == 0b10); Label valid_global_tag; test_bit(tmp1, value, exact_log2(JNIHandles::TypeTag::global)); // Test for global tag. bnez(tmp1, valid_global_tag); @@ -3050,8 +3050,8 @@ void MacroAssembler::pop_CPU_state(bool restore_vectors, int vector_size_in_byte } static int patch_offset_in_jal(address branch, int64_t offset) { - assert(Assembler::is_simm21(offset) && ((offset % 2) == 0), - "offset (%ld) is too large to be patched in one jal instruction!\n", offset); + guarantee(Assembler::is_simm21(offset) && ((offset % 2) == 0), + "offset (%ld) is too large to be patched in one jal instruction!", offset); Assembler::patch(branch, 31, 31, (offset >> 20) & 0x1); // offset[20] ==> branch[31] Assembler::patch(branch, 30, 21, (offset >> 1) & 0x3ff); // offset[10:1] ==> branch[30:21] Assembler::patch(branch, 20, 20, (offset >> 11) & 0x1); // offset[11] ==> branch[20] @@ -3060,8 +3060,8 @@ static int patch_offset_in_jal(address branch, int64_t offset) { } static int patch_offset_in_conditional_branch(address branch, int64_t offset) { - assert(Assembler::is_simm13(offset) && ((offset % 2) == 0), - "offset (%ld) is too large to be patched in one beq/bge/bgeu/blt/bltu/bne instruction!\n", offset); + guarantee(Assembler::is_simm13(offset) && ((offset % 2) == 0), + "offset (%ld) is too large to be patched in one beq/bge/bgeu/blt/bltu/bne instruction!", offset); Assembler::patch(branch, 31, 31, (offset >> 12) & 0x1); // offset[12] ==> branch[31] Assembler::patch(branch, 30, 25, (offset >> 5) & 0x3f); // offset[10:5] ==> branch[30:25] Assembler::patch(branch, 7, 7, (offset >> 11) & 0x1); // offset[11] ==> branch[7] diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp index 6f51395898a3..1f757f805b64 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp @@ -50,36 +50,11 @@ bool NativeInstruction::is_call_at(address addr) { //----------------------------------------------------------------------------- // NativeCall +// The destination of a reloc call is kept in its address stub. +// Returns nullptr as long as the call has no address stub, see stub_address(). address NativeCall::destination() const { - address addr = instruction_address(); - assert(NativeCall::is_at(addr), "unexpected code at call site"); - - address stub_addr = MacroAssembler::target_addr_for_insn(addr); - - CodeBlob* cb = CodeCache::find_blob(addr); - assert(cb != nullptr && cb->is_nmethod(), "nmethod expected"); - nmethod *nm = (nmethod *)cb; - assert(nm->stub_contains(stub_addr), "Sanity"); - assert(stub_addr != nullptr, "Sanity"); - - return stub_address_destination_at(stub_addr); -} - -address NativeCall::reloc_destination() { - address call_addr = instruction_address(); - assert(NativeCall::is_at(call_addr), "unexpected code at call site"); - - CodeBlob *code = CodeCache::find_blob(call_addr); - assert(code != nullptr, "Could not find the containing code blob"); - - address stub_addr = nullptr; - if (code->is_nmethod()) { - // TODO: Need to revisit this when porting the AOT features. - stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()); - assert(stub_addr != nullptr, "Sanity"); - } - - return stub_addr; + address stub_addr = stub_address(); + return stub_addr != nullptr ? stub_address_destination_at(stub_addr) : nullptr; } void NativeCall::verify() { @@ -123,41 +98,33 @@ bool NativeCall::set_destination_mt_safe(address dest) { address stub_addr = stub_address(); assert(stub_addr != nullptr, "No stub?"); - set_stub_address_destination_at(stub_addr, dest); // release - // optimize_call happens after we stored new address in addr stub. + set_stub_address_destination_at(stub_addr, dest); + // Release: the new stub destination must be visible before the jal/jalr + // patch in optimize_call becomes visible to concurrent threads. + OrderAccess::release(); // patches jalr -> jal/jal -> jalr depending on dest optimize_call(dest, true); return true; } -// The argument passed in is the address to the stub containing the destination -bool NativeCall::reloc_set_destination(address stub_addr) { +void NativeCall::set_destination(address dest) { address call_addr = instruction_address(); assert(NativeCall::is_at(call_addr), "unexpected code at call site"); - CodeBlob *code = CodeCache::find_blob(call_addr); - assert(code != nullptr, "Could not find the containing code blob"); - - if (code->is_nmethod()) { - // TODO: Need to revisit this when porting the AOT features. - assert(stub_addr != nullptr, "Sanity"); - assert(stub_addr == trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()), "Sanity"); - MacroAssembler::pd_patch_instruction_size(call_addr, stub_addr); // patches auipc + ld to stub_addr - - address dest = stub_address_destination_at(stub_addr); - optimize_call(dest, false); // patches jalr -> jal/jal -> jalr depending on dest - } - - return true; + address stub_addr = stub_address(); + assert(stub_addr != nullptr, "No stub?"); + set_stub_address_destination_at(stub_addr, dest); + // patches auipc + ld to stub_addr + MacroAssembler::pd_patch_instruction_size(call_addr, stub_addr); + // patches jalr -> jal/jal -> jalr depending on dest + optimize_call(dest, false); } void NativeCall::set_stub_address_destination_at(address dest, address value) { assert_cond(dest != nullptr); assert_cond(value != nullptr); - set_data64_at(dest, (uint64_t)value); - OrderAccess::release(); } address NativeCall::stub_address_destination_at(address src) { @@ -166,15 +133,35 @@ address NativeCall::stub_address_destination_at(address src) { return dest; } -address NativeCall::stub_address() { +// Returns the address stub of this reloc call, or nullptr if the call does not +// have an address stub yet, which is the case while code is being emitted into +// a CodeBuffer. +address NativeCall::stub_address() const { address call_addr = instruction_address(); + assert(NativeCall::is_at(call_addr), "unexpected code at call site"); + + address stub_addr = MacroAssembler::target_addr_for_insn(call_addr); + if (stub_addr != call_addr) { + // The call has been linked to its address stub. +#ifdef ASSERT + CodeBlob *code = CodeCache::find_blob(call_addr); + assert(code != nullptr && code->contains(stub_addr), "Sanity"); +#endif + return stub_addr; + } + // The auipc + ld pair still points to itself, i.e. the call has not been + // linked to its address stub yet. This is the case when we are relocating + // freshly generated code, where the stub can only be found through the + // relocation info. See Relocation::pd_set_call_destination. CodeBlob *code = CodeCache::find_blob(call_addr); - assert(code != nullptr, "Could not find the containing code blob"); + if (code == nullptr || !code->is_nmethod()) { + // Code is still living in a CodeBuffer, there is no relocation info to + // search and nothing to patch yet. + return nullptr; + } - address stub_addr = MacroAssembler::target_addr_for_insn(call_addr); - assert(code->contains(stub_addr), "Sanity"); - return stub_addr; + return trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()); } bool NativeCall::is_at(address addr) { diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index 90f32c9b25db..ac807d00134e 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -133,25 +133,24 @@ class NativeCall: private NativeInstruction { address instruction_address() const { return addr_at(0); } address next_instruction_address() const { return addr_at(NativeCall::instruction_size); } address return_address() const { return addr_at(NativeCall::instruction_size); } + // return target address of the reloc call, read from its address stub address destination() const; - address reloc_destination(); void verify_alignment() {} // do nothing on riscv void verify(); void print(); - void set_destination(address dest) { Unimplemented(); } + // patch the address stub and link the reloc call to it + void set_destination(address dest); // patch stub to target address of the reloc call bool set_destination_mt_safe(address dest); - // patch reloc call to stub address - bool reloc_set_destination(address dest); static bool is_at(address addr); static bool is_call_before(address return_address); private: - // return stub address, without checking stub address in locs - address stub_address(); + // return the address stub of the reloc call, nullptr if there is none yet + address stub_address() const; // set target address at stub static void set_stub_address_destination_at(address dest, address value); // return target address at stub diff --git a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp index 09264327516e..f7793ce81e35 100644 --- a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp +++ b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp @@ -74,7 +74,12 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { address Relocation::pd_call_destination(address orig_addr) { assert(is_call(), "should be a call here"); if (NativeCall::is_at(addr())) { - return nativeCall_at(addr())->reloc_destination(); + // A reloc call keeps its destination in its address stub, so the + // instruction sequence at orig_addr must not be decoded here: the + // auipc + ld pair points to itself until the call is linked to the + // stub in pd_set_call_destination. + NativeCall* call = nativeCall_at(addr()); + return call->destination(); } if (orig_addr != nullptr) { @@ -93,12 +98,16 @@ address Relocation::pd_call_destination(address orig_addr) { void Relocation::pd_set_call_destination(address x) { assert(is_call(), "should be a call here"); if (NativeCall::is_at(addr())) { - NativeCall* call = nativeCall_at(addr()); - call->reloc_set_destination(x); + // Nothing to do while code is still living in a CodeBuffer, where the call + // does not have an address stub yet. See NativeCall::stub_address(). + if (x != nullptr) { + NativeCall* call = nativeCall_at(addr()); + call->set_destination(x); + } } else { MacroAssembler::pd_patch_instruction_size(addr(), x); - assert(pd_call_destination(addr()) == x, "fail in reloc"); } + guarantee(pd_call_destination(addr()) == x, "fail in reloc"); } address* Relocation::pd_address_in_code() { diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 8e42db38cfa9..361755ba2fc8 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1311,8 +1311,6 @@ bool unnecessary_release(const Node *barrier) return false; } -constexpr uint64_t MAJIK_DWORD = 0xabbaabbaabbaabbaull; - // predicate controlling translation of CAS // // returns true if CAS needs to use an acquiring load otherwise false @@ -1505,11 +1503,6 @@ void MachPrologNode::format(PhaseRegAlloc *ra_, outputStream *st) const { st->print("sd ra, [sp, #%d]\n\t", framesize - wordSize); if (PreserveFramePointer) { st->print("add fp, sp, #%d\n\t", framesize); } - if (VerifyStackAtCalls) { - st->print("mv t2, %ld\n\t", MAJIK_DWORD); - st->print("sd t2, [sp, #%d]\n\t", framesize - 3 * wordSize); - } - if (C->stub_function() == nullptr) { st->print("ld t0, [guard]\n\t"); st->print("membar LoadLoad\n\t"); @@ -1529,13 +1522,6 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const { __ verified_entry(C, 0); - if (VerifyStackAtCalls) { - // n.b. frame size includes space for return pc and fp - const long framesize = C->output()->frame_size_in_bytes(); - __ mv(t2, MAJIK_DWORD); - __ sd(t2, Address(sp, framesize - 3 * wordSize)); - } - if (C->stub_function() == nullptr) { __ entry_barrier(); } @@ -2532,18 +2518,7 @@ encode %{ __ post_call_nop(); %} - enc_class riscv_enc_call_epilog() %{ - if (VerifyStackAtCalls) { - // Check that stack depth is unchanged: find majik cookie on stack - int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3 * VMRegImpl::slots_per_word)); - Label stack_ok; - __ ld(t1, Address(sp, framesize)); - __ mv(t2, MAJIK_DWORD); - __ beq(t2, t1, stack_ok); - __ stop("MAJIK_DWORD not found"); - __ bind(stack_ok); - } - %} + enc_class riscv_enc_call_epilog() %{ %} enc_class riscv_enc_java_to_runtime(method meth) %{ Assembler::IncompressibleScope scope(masm); // Fixed length: see ret_addr_offset diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 07d2ba4af0a6..417bf442814c 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -498,7 +498,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, OopMapSet* oop_maps, int& frame_complete, int& frame_size_in_words, - bool alloc_inline_receiver) { + bool alloc_value_receiver) { if (requires_clinit_barrier) { assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; @@ -755,7 +755,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, if (regs_cc != regs_cc_ro) { // No class init barrier needed because method is guaranteed to be non-static gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); skip_fixup.reset(); } @@ -763,7 +763,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I] = __ pc(); entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ true); // Non-scalarized c2i adapter if (regs != regs_cc) { @@ -773,7 +773,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); } // The c2i adapters might safepoint and trigger a GC. The caller must make sure that @@ -2429,7 +2429,7 @@ void SharedRuntime::generate_deopt_blob() { // EPILOG must remove this many slots. // RISCV needs two words for RA (return address) and FP (frame pointer). uint SharedRuntime::in_preserve_stack_slots() { - return 2 * VMRegImpl::slots_per_word + (VerifyStackAtCalls ? 0 : 2) ; + return 2 * VMRegImpl::slots_per_word; } uint SharedRuntime::out_preserve_stack_slots() { diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 4124b6538094..a84d2b46341a 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -2965,7 +2965,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr __ store_heap_oop(field, x10, x28, x29, x13, IN_HEAP); __ j(Done); } else { - Label null_free_reference, is_flat, rewrite_inline; + Label null_free_reference, is_flat, rewrite_value; __ test_field_is_flat(flags, x28, is_flat); __ test_field_is_null_free_value_type(flags, x28, null_free_reference); pop_and_check_object(obj); @@ -2990,11 +2990,11 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr // Store into the field __ store_heap_oop(field, x10, x28, x29, x13, IN_HEAP); } - __ j(rewrite_inline); + __ j(rewrite_value); __ bind(is_flat); pop_and_check_object(x17); __ write_flat_field(cache, off, index, flags, x17); - __ bind(rewrite_inline); + __ bind(rewrite_value); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_vputfield, bc, x9, true, byte_no); } diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 0d434c87d74d..3527b243192e 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -354,7 +354,7 @@ class VM_Version : public Abstract_VM_Version { }; private: uint64_t _features_bitmap[(MAX_CPU_FEATURE_INDEX / BitsPerLong) + 1]; - STATIC_ASSERT(sizeof(_features_bitmap) * BitsPerByte >= MAX_CPU_FEATURE_INDEX); + static_assert(sizeof(_features_bitmap) * BitsPerByte >= MAX_CPU_FEATURE_INDEX); // Number of 8-byte elements in _features_bitmap. constexpr static int element_count() { diff --git a/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp b/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp index 4cfcd39fda64..ae24eead152f 100644 --- a/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp +++ b/src/hotspot/cpu/riscv/vtableStubs_riscv.cpp @@ -80,7 +80,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) #endif // get receiver (need to skip return address on top of stack) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); // get receiver klass address npe_addr = __ pc(); @@ -182,7 +182,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) #endif // get receiver (need to skip return address on top of stack) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); // Arguments from this point: // t1 (moved from t0): CompiledICData diff --git a/src/hotspot/cpu/s390/frame_s390.inline.hpp b/src/hotspot/cpu/s390/frame_s390.inline.hpp index 5d0756b35586..1ac0408b2b0b 100644 --- a/src/hotspot/cpu/s390/frame_s390.inline.hpp +++ b/src/hotspot/cpu/s390/frame_s390.inline.hpp @@ -376,6 +376,9 @@ inline frame frame::sender(RegisterMap* map) const { StackWatermarkSet::on_iteration(map->thread(), result); } + // Calling frame::id() is currently not supported for heap frames. + assert(result._on_heap || this->_on_heap || result.is_older(this->id()), "Must be"); + return result; } diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 16ade81a8200..9d5ab8d3e473 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -1062,7 +1062,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, OopMapSet* oop_maps, int& frame_complete, int& frame_size_in_words, - bool alloc_inline_receiver) { + bool alloc_value_receiver) { if (requires_clinit_barrier) { assert(VM_Version::supports_fast_class_init_checks(), "sanity"); @@ -1132,7 +1132,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, __ z_lgr(Z_ARG1, Z_thread); __ z_lgr(Z_ARG2, Z_method); - __ load_const_optimized(Z_ARG3, (intptr_t)alloc_inline_receiver); + __ load_const_optimized(Z_ARG3, (intptr_t)alloc_value_receiver); __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::allocate_value_types), Z_ARG1, Z_ARG2, Z_ARG3); // TODO: use blob-relative offset, matching the pattern in the rest of @@ -3155,7 +3155,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, __ unimplemented("C2I_Value_RO"); // No class init barrier needed because method is guaranteed to be non-static gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); skip_fixup.reset(); } @@ -3163,7 +3163,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I] = __ pc(); entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ true); // Non-scalarized c2i adapter if (regs != regs_cc) { @@ -3175,7 +3175,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I_Value] = __ pc(); __ unimplemented("C2I_Value2"); gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); } // The c2i adapters might safepoint and trigger a GC. The caller must make sure that diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index e9deac670dc2..364a60b2455e 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -3350,7 +3350,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr do_oop_store(_masm, field, Z_tos, oopStore_tmp1, oopStore_tmp2, oopStore_tmp3, IN_HEAP); } else { - Label null_free_reference, is_flat, rewrite_inline, done_valhalla; + Label null_free_reference, is_flat, rewrite_value, done_valhalla; __ z_tmll(flags, 1 << ResolvedFieldEntry::is_flat_shift); __ branch_optimized(Assembler::bcondAllOne, is_flat); __ z_tmll(flags, 1 << ResolvedFieldEntry::is_null_free_value_type_shift); @@ -3372,7 +3372,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr // Store into the field do_oop_store(_masm, field, Z_tos, oopStore_tmp1, oopStore_tmp2, oopStore_tmp3, IN_HEAP); - __ z_bru(rewrite_inline); + __ z_bru(rewrite_value); __ bind(is_flat); pop_and_check_object(oopStore_tmp1); { @@ -3386,7 +3386,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr Register flat_index = oopStore_tmp2; // Z_R1_scratch (index scratch, discarded after load) __ load_field_entry(flat_entry, flat_index); __ write_flat_field(flat_entry, off, flat_index, oopStore_tmp3, oopStore_tmp1); - }__ bind(rewrite_inline); + }__ bind(rewrite_value); if (do_rewrite) { patch_bytecode(Bytecodes::_fast_vputfield, bc_reg, patch_tmp, true, byte_no); } diff --git a/src/hotspot/cpu/s390/vtableStubs_s390.cpp b/src/hotspot/cpu/s390/vtableStubs_s390.cpp index 596f3bc1c229..23747e18815f 100644 --- a/src/hotspot/cpu/s390/vtableStubs_s390.cpp +++ b/src/hotspot/cpu/s390/vtableStubs_s390.cpp @@ -78,7 +78,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == Z_R2->as_VMReg(), "receiver expected in Z_ARG1"); + assert(SharedRuntime::name_for_receiver() == Z_R2->as_VMReg(), "receiver expected in Z_ARG1"); const Register rcvr_klass = Z_R1_scratch; address npe_addr = __ pc(); // npe is short for null pointer exception @@ -181,7 +181,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) } #endif - assert(VtableStub::receiver_location() == Z_R2->as_VMReg(), "receiver expected in Z_ARG1"); + assert(SharedRuntime::name_for_receiver() == Z_R2->as_VMReg(), "receiver expected in Z_ARG1"); // Entry arguments: // Z_method: Interface diff --git a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp index a1459a34f52e..1b33ee683e41 100644 --- a/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp +++ b/src/hotspot/cpu/x86/c1_Runtime1_x86.cpp @@ -918,7 +918,7 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) { case StubId::c1_new_null_free_array_id: __ cmpl(t0, Klass::_lh_array_tag_flat_value); // the array can be a flat array. __ jcc(Assembler::equal, ok); - __ cmpl(t0, (Klass::_lh_array_tag_ref_value)); // the array cannot be a flat array (due to InlineArrayElementMaxFlatSize, etc) + __ cmpl(t0, (Klass::_lh_array_tag_ref_value)); // the array cannot be a flat array. __ jcc(Assembler::equal, ok); __ stop("assert(is an object or value type array klass)"); break; diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index e69a210117ef..58c166ed2b15 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -136,25 +136,6 @@ void C2_MacroAssembler::verified_entry(Compile* C, int sp_inc) { assert((sp_inc & (StackAlignmentInBytes-1)) == 0, "stack increment not aligned"); movptr(Address(rsp, framesize - wordSize), sp_inc + framesize); } - - if (VerifyStackAtCalls) { // Majik cookie to verify stack depth - framesize -= wordSize; - movptr(Address(rsp, framesize), (int32_t)0xbadb100d); - } - -#ifdef ASSERT - if (VerifyStackAtCalls) { - Label L; - push(rax); - mov(rax, rsp); - andptr(rax, StackAlignmentInBytes-1); - cmpptr(rax, StackAlignmentInBytes-wordSize); - pop(rax); - jcc(Assembler::equal, L); - STOP("Stack is not properly aligned!"); - bind(L); - } -#endif } void C2_MacroAssembler::entry_barrier() { diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp index 15db93064c55..3bdf407b41a1 100644 --- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp @@ -180,7 +180,7 @@ static void generate_pre_barrier_slow_path(MacroAssembler* masm, Address buffer_addr(thread, in_bytes(G1ThreadLocalData::satb_mark_queue_buffer_offset())); // This code assumes that buffer index is pointer sized. - STATIC_ASSERT(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); + static_assert(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); Label L_runtime; @@ -501,7 +501,7 @@ void G1BarrierSetAssembler::g1_write_barrier_post_c1(MacroAssembler* masm, void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { // Generated code assumes that buffer index is pointer sized. - STATIC_ASSERT(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); + static_assert(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t)); __ prologue("g1_pre_barrier", false); // arg0 : previous value of memory diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 8e0ac8b92cf1..0ef885fbffde 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -31,9 +31,11 @@ #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "interpreter/interpreter.hpp" +#include "nativeInst_x86.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/macros.hpp" @@ -44,6 +46,7 @@ #endif #ifdef COMPILER2 #include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#include "opto/output.hpp" #endif #define __ masm-> @@ -590,6 +593,53 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #undef __ +address ShenandoahBarrierSetAssembler::parse_jump_address(address pc) { + NativeInstruction* ni = nativeInstruction_at(pc); + assert(ni->is_jump(), "Must be a jump"); + NativeJump* jmp = nativeJump_at(pc); + return jmp->jump_destination(); +} + +void ShenandoahBarrierSetAssembler::insert_patchable_nop(address pc) { + *(pc + 0) = 0x0F; + *(pc + 1) = 0x1F; + *(pc + 2) = 0x44; + *(pc + 3) = 0x00; + *(pc + 4) = 0x00; + // No invalidation is needed: relies on reader-side cross-modify-fence. +} + +bool ShenandoahBarrierSetAssembler::is_patchable_nop(address pc) { + if (*(pc + 0) != 0x0F) return false; + if (*(pc + 1) != 0x1F) return false; + if (*(pc + 2) != 0x44) return false; + if (*(pc + 3) != 0x00) return false; + if (*(pc + 4) != 0x00) return false; + return true; +} + +void ShenandoahBarrierSetAssembler::insert_patchable_jump(address pc, address target_pc) { + int32_t disp = checked_cast((intptr_t)target_pc - ((intptr_t)pc + 5)); + + *(pc + 0) = 0xE9; + *(pc + 1) = (disp >> 0) & 0xFF; + *(pc + 2) = (disp >> 8) & 0xFF; + *(pc + 3) = (disp >> 16) & 0xFF; + *(pc + 4) = (disp >> 24) & 0xFF; + // No invalidation is needed: relies on reader-side cross-modify-fence. +} + +bool ShenandoahBarrierSetAssembler::is_patchable_jump(address pc, address target_pc) { + int32_t disp = checked_cast((intptr_t)target_pc - ((intptr_t)pc + 5)); + + if (*(pc + 0) != 0xE9) return false; + if (*(pc + 1) != ((disp >> 0) & 0xFF)) return false; + if (*(pc + 2) != ((disp >> 8) & 0xFF)) return false; + if (*(pc + 3) != ((disp >> 16) & 0xFF)) return false; + if (*(pc + 4) != ((disp >> 24) & 0xFF)) return false; + return true; +} + #ifdef COMPILER1 #define __ ce->masm()-> @@ -778,15 +828,51 @@ void ShenandoahBarrierStubC2::cardtable(MacroAssembler& masm, Address addr, Regi __ bind(L_done); } -void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) { - Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); +void ShenandoahBarrierStubC2::patchable_jump(MacroAssembler& masm, const char gc_state, bool jump_when_state, Register tmp1, Register tmp2, Label* L_target) { +#ifdef ASSERT + Label L_fake_entry, L_good; + Address gc_state_addr(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); + + // Emit the secondary jump and use it to cross-check against the actual GC state. + // This also checks that all interesting GC state transitions are done non-racily + // from the perspective of the thread executing the nmethod. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ jmp(L_fake_entry, /* maybe_short = */ false); + + // Currently hot-patched to NOP. + __ testb(gc_state_addr, gc_state); + __ jccb(jump_when_state ? Assembler::zero : Assembler::notZero, L_good); + __ hlt(); + + // Currently hot-patched to JUMP. + __ bind(L_fake_entry); + __ testb(gc_state_addr, gc_state); + __ jccb(jump_when_state ? Assembler::notZero : Assembler::zero, L_good); + __ hlt(); + + __ bind(L_good); +#endif + + PhaseOutput* const output = Compile::current()->output(); + if (!output->in_scratch_emit_size()) { + // Emit the unconditional branch in the first version of the method. + // Let the rest of runtime figure out how to manage it. + __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(gc_state, jump_when_state))); + __ jmp(*L_target, /* maybe_short = */ false); + } else { + // Avoid binding L_target in scratch emits. + // We know the patchable check is exactly 5 bytes long. + __ nop(5); + } +} - Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state))); - __ cmpb(gc_state_fast, 0); - __ jcc(Assembler::notEqual, *entry()); +void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp1, Register tmp2) { + Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); + patchable_jump_if_gc_state(masm, test_state, tmp1, tmp2, entry()); __ bind(*continuation()); } + void ShenandoahBarrierStubC2::emit_code(MacroAssembler& masm) { Assembler::InlineSkippedInstructionsCounter skip_counter(&masm); assert(_needs_keep_alive_barrier || _needs_load_ref_barrier, "Why are you here?"); @@ -831,17 +917,16 @@ void ShenandoahBarrierStubC2::emit_code(MacroAssembler& masm) { } void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { - Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING))); Address index(r15_thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); Address buffer(r15_thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); Label L_through, L_pop_and_slow; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_load_ref_barrier) { - assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true"); - __ cmpb(gc_state_fast, 0); - __ jcc(Assembler::equal, L_through); + assert(L_done == nullptr, "Should be"); + char state_to_check = ShenandoahHeap::MARKING; + patchable_jump_if_not_gc_state(masm, state_to_check, noreg, noreg, &L_through); } // Need temp to work, allocate one now. @@ -911,20 +996,17 @@ void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) { void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) { Label L_pop_and_slow, L_slow; - // If another barrier is enabled as well, do a runtime check for a specific barrier. + // If another barrier is enabled as well, do a check for a specific barrier. if (_needs_keep_alive_barrier) { char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0); - Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check))); - __ cmpb(gc_state_fast, 0); - __ jcc(Assembler::equal, *continuation()); + patchable_jump_if_not_gc_state(masm, state_to_check, noreg, noreg, continuation()); } // If weak references are being processed, weak/phantom loads need to go slow, // regardless of their cset status. if (_needs_load_ref_weak_barrier) { - Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS))); - __ cmpb(gc_state_fast, 0); - __ jccb(Assembler::notEqual, L_slow); + char state_to_check = ShenandoahHeap::WEAK_ROOTS; + patchable_jump_if_gc_state(masm, state_to_check, noreg, noreg, &L_slow); } bool is_aot = AOTCodeCache::is_on_for_dump(); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 0ef369a6b79d..a6d9b8141747 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -73,6 +73,13 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath); virtual void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error); + // Jumps hotpatching + static address parse_jump_address(address pc); + static void insert_patchable_nop(address pc); + static bool is_patchable_nop(address pc); + static void insert_patchable_jump(address pc, address target_pc); + static bool is_patchable_jump(address pc, address target_pc); + #ifdef COMPILER1 void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 55be1645e2d8..ae0e02783b22 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -3513,7 +3513,7 @@ void MacroAssembler::vpermd(XMMRegister dst, XMMRegister nds, AddressLiteral sr void MacroAssembler::clear_jobject_tag(Register possibly_non_local) { const int32_t inverted_mask = ~static_cast(JNIHandles::tag_mask); - STATIC_ASSERT(inverted_mask == -4); // otherwise check this code + static_assert(inverted_mask == -4); // otherwise check this code // The inverted mask is sign-extended andptr(possibly_non_local, inverted_mask); } diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index f6a9155d5adc..6c76ddcc65d0 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -874,7 +874,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, OopMapSet* oop_maps, int& frame_complete, int& frame_size_in_words, - bool alloc_inline_receiver) { + bool alloc_value_receiver) { if (requires_clinit_barrier) { assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; @@ -928,7 +928,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, __ mov(c_rarg0, r15_thread); __ mov(c_rarg1, rbx); - __ mov64(c_rarg2, (int64_t)alloc_inline_receiver); + __ mov64(c_rarg2, (int64_t)alloc_value_receiver); __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::allocate_value_types))); oop_maps->add_gc_map((int)(__ pc() - start), map); @@ -1026,7 +1026,7 @@ static void gen_c2i_adapter(MacroAssembler *masm, int vt = 1; // write fields we get from compiled code in registers/stack // slots to the buffer: we know we are done with that value type - // argument when we hit the T_VOID that acts as an end of inline + // argument when we hit the T_VOID that acts as an end of value // type delimiter for this value type. Value types are flattened // so we might encounter embedded value types. Each entry in // sig_extended contains a field offset in the buffer. @@ -1315,7 +1315,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, if (regs_cc != regs_cc_ro) { // No class init barrier needed because method is guaranteed to be non-static gen_c2i_adapter(masm, sig_cc_ro, regs_cc_ro, /* requires_clinit_barrier = */ false, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); skip_fixup.reset(); } @@ -1323,7 +1323,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I] = __ pc(); entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig_cc, regs_cc, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ true); + skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ true); // Non-scalarized c2i adapter if (regs != regs_cc) { @@ -1333,7 +1333,7 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler* masm, entry_address[AdapterBlob::C2I_Value] = __ pc(); gen_c2i_adapter(masm, sig, regs, /* requires_clinit_barrier = */ true, entry_address[AdapterBlob::C2I_No_Clinit_Check], - value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_inline_receiver = */ false); + value_entry_skip_fixup, entry_address[AdapterBlob::I2C], oop_maps, frame_complete, frame_size_in_words, /* alloc_value_receiver = */ false); } // The c2i adapters might safepoint and trigger a GC. The caller must make sure that @@ -2742,7 +2742,7 @@ uint SharedRuntime::out_preserve_stack_slots() { // EPILOG must remove this many slots. amd64 needs two slots for // return address. uint SharedRuntime::in_preserve_stack_slots() { - return 4 + 2 * VerifyStackAtCalls; + return 2 * VMRegImpl::slots_per_word; } VMReg SharedRuntime::thread_register() { diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 0d945d5b554f..f5c4a1c1b20f 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -2978,7 +2978,7 @@ void TemplateTable::putfield_or_static_helper(int byte_no, bool is_static, Rewri do_oop_store(_masm, field, rax); __ jmp(Done); } else { - Label is_flat, null_free_reference, rewrite_inline; + Label is_flat, null_free_reference, rewrite_value; __ test_field_is_flat(flags, rscratch1, is_flat); __ test_field_is_null_free_value_type(flags, rscratch1, null_free_reference); pop_and_check_object(obj); @@ -2993,11 +2993,11 @@ void TemplateTable::putfield_or_static_helper(int byte_no, bool is_static, Rewri pop_and_check_object(obj); // Store into the field do_oop_store(_masm, field, rax); - __ jmp(rewrite_inline); + __ jmp(rewrite_value); __ bind(is_flat); pop_and_check_object(rscratch2); __ write_flat_field(rcx, r8, rscratch1, rscratch2, rbx, rax); - __ bind(rewrite_inline); + __ bind(rewrite_value); if (rc == may_rewrite) { patch_bytecode(Bytecodes::_fast_vputfield, bc, rbx, true, byte_no); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 459180b2fe62..17862a008c70 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -454,7 +454,7 @@ class VM_Version : public Abstract_VM_Version { private: uint64_t _features_bitmap[(MAX_CPU_FEATURES / BitsPerLong) + 1]; - STATIC_ASSERT(sizeof(_features_bitmap) * BitsPerByte >= MAX_CPU_FEATURES); + static_assert(sizeof(_features_bitmap) * BitsPerByte >= MAX_CPU_FEATURES); // Number of 8-byte elements in _bitmap. constexpr static int features_bitmap_element_count() { diff --git a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp index 9cd94636a774..657c2d70cd8d 100644 --- a/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp +++ b/src/hotspot/cpu/x86/vtableStubs_x86_64.cpp @@ -74,7 +74,7 @@ VtableStub* VtableStubs::create_vtable_stub(int vtable_index, bool caller_is_c1) #endif // get receiver (need to skip return address on top of stack) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); // Free registers (non-args) are rax, rbx @@ -188,7 +188,7 @@ VtableStub* VtableStubs::create_itable_stub(int itable_index, bool caller_is_c1) Label L_no_such_interface; // get receiver klass (also an implicit null-check) - assert(VtableStub::receiver_location() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); + assert(SharedRuntime::name_for_receiver() == j_rarg0->as_VMReg(), "receiver expected in j_rarg0"); address npe_addr = __ pc(); __ load_klass(recv_klass_reg, j_rarg0, temp_reg); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index f48a7d0eb777..d54af755c819 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1866,15 +1866,6 @@ void MachPrologNode::format(PhaseRegAlloc* ra_, outputStream* st) const { } } - if (VerifyStackAtCalls) { - st->print("\n\t"); - framesize -= wordSize; - st->print("movq [rsp + #%d], 0xbadb100d\t# Majik cookie for stack depth check",framesize); -#ifdef ASSERT - st->print("\n\t"); - st->print("# stack alignment check"); -#endif - } if (C->stub_function() != nullptr) { st->print("\n\t"); st->print("cmpl [r15_thread + #disarmed_guard_value_offset], #disarmed_guard_value\t"); @@ -4611,16 +4602,6 @@ encode %{ %} enc_class call_epilog %{ - if (VerifyStackAtCalls) { - // Check that stack depth is unchanged: find majik cookie on stack - int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3*VMRegImpl::slots_per_word)); - Label L; - __ cmpptr(Address(rsp, framesize), (int32_t)0xbadb100d); - __ jccb(Assembler::equal, L); - // Die if stack mismatch - __ int3(); - __ bind(L); - } if (tf()->returns_value_type_as_fields() && !_method->is_method_handle_intrinsic() && _method->return_type()->is_loaded()) { // The last return value is not set by the callee but used to pass the null marker to compiled code. // Search for the corresponding projection, get the register and emit code that initializes it. diff --git a/src/hotspot/cpu/zero/gc/shenandoah/shenandoahBarrierSetAssembler_zero.hpp b/src/hotspot/cpu/zero/gc/shenandoah/shenandoahBarrierSetAssembler_zero.hpp index a0ea936dacda..b863ac67ee74 100644 --- a/src/hotspot/cpu/zero/gc/shenandoah/shenandoahBarrierSetAssembler_zero.hpp +++ b/src/hotspot/cpu/zero/gc/shenandoah/shenandoahBarrierSetAssembler_zero.hpp @@ -25,6 +25,28 @@ #ifndef CPU_ZERO_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_ZERO_HPP #define CPU_ZERO_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_ZERO_HPP -class ShenandoahBarrierSetAssembler; +#include "utilities/globalDefinitions.hpp" + +class ShenandoahBarrierSetAssembler { +public: + static address parse_jump_address(address pc) { + ShouldNotCallThis(); + return nullptr; + } + static void insert_patchable_nop(address pc) { + ShouldNotCallThis(); + } + static bool is_patchable_nop(address pc) { + ShouldNotCallThis(); + return false; + } + static void insert_patchable_jump(address pc, address target_pc) { + ShouldNotCallThis(); + } + static bool is_patchable_jump(address pc, address target_pc) { + ShouldNotCallThis(); + return false; + } +}; #endif // CPU_ZERO_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_ZERO_HPP diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index ae85be96b97c..d8e20c37ee83 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -99,7 +99,7 @@ #ifndef MAP_ANON_64K #define MAP_ANON_64K 0x400 #else - STATIC_ASSERT(MAP_ANON_64K == 0x400); + static_assert(MAP_ANON_64K == 0x400); #endif #include #include @@ -1089,7 +1089,7 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { if (result == nullptr && eno == ENOENT) { const char* pointer_to_dot = strrchr(filename, '.'); if (pointer_to_dot != nullptr && strcmp(pointer_to_dot, old_extension) == 0) { - STATIC_ASSERT(sizeof(old_extension) >= sizeof(new_extension)); + static_assert(sizeof(old_extension) >= sizeof(new_extension)); char* tmp_path = os::strdup(filename); size_t prefix_size = pointer_delta(pointer_to_dot, filename, 1); os::snprintf_checked(tmp_path + prefix_size, sizeof(old_extension), "%s", new_extension); diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index f0527136d90f..de352ac0c09f 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -947,9 +947,9 @@ static const char* rtv_linkedin_libpath() { struct scnhdr the_scn; struct ldhdr the_ldr; constexpr size_t xcoffsz = FILHSZ + _AOUTHSZ_EXEC; - STATIC_ASSERT(sizeof(the_xcoff) == xcoffsz); - STATIC_ASSERT(sizeof(the_scn) == SCNHSZ); - STATIC_ASSERT(sizeof(the_ldr) == LDHDRSZ); + static_assert(sizeof(the_xcoff) == xcoffsz); + static_assert(sizeof(the_scn) == SCNHSZ); + static_assert(sizeof(the_ldr) == LDHDRSZ); // read the generic XCOFF header and analyze the substructures // to find the burned in libpath. In any case of error perform the assert if (nullptr == (f = fopen(buffer, "r")) || diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 18c1c1eb26f7..b9f95228c810 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -41,7 +41,7 @@ #ifndef CGROUP2_SUPER_MAGIC # define CGROUP2_SUPER_MAGIC 0x63677270 #else - STATIC_ASSERT(CGROUP2_SUPER_MAGIC == 0x63677270); + static_assert(CGROUP2_SUPER_MAGIC == 0x63677270); #endif // controller names have to match the *_IDX indices diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 12a4ea2bda47..1d66aa90dc68 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -3094,7 +3094,7 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, #define MADV_POPULATE_WRITE MADV_POPULATE_WRITE_value #else // Sanity-check our assumed default value if we build with a new enough libc. - STATIC_ASSERT(MADV_POPULATE_WRITE == MADV_POPULATE_WRITE_value); + static_assert(MADV_POPULATE_WRITE == MADV_POPULATE_WRITE_value); #endif // Note that the value for MAP_FIXED_NOREPLACE differs between architectures, but all architectures @@ -3104,7 +3104,7 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, #define MAP_FIXED_NOREPLACE MAP_FIXED_NOREPLACE_value #else // Sanity-check our assumed default value if we build with a new enough libc. - STATIC_ASSERT(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value); + static_assert(MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE_value); #endif int os::Linux::commit_memory_impl(char* addr, size_t size, diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index bdce5c40febf..9daebf91604b 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -53,7 +53,7 @@ #define SEGV_BNDERR_value 3 #if defined(SEGV_BNDERR) -STATIC_ASSERT(SEGV_BNDERR == SEGV_BNDERR_value); +static_assert(SEGV_BNDERR == SEGV_BNDERR_value); #else #define SEGV_BNDERR SEGV_BNDERR_value #endif diff --git a/src/hotspot/os_cpu/bsd_aarch64/atomicAccess_bsd_aarch64.hpp b/src/hotspot/os_cpu/bsd_aarch64/atomicAccess_bsd_aarch64.hpp index 67701775f940..b6f6146c493e 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/atomicAccess_bsd_aarch64.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/atomicAccess_bsd_aarch64.hpp @@ -60,8 +60,8 @@ template inline T AtomicAccess::PlatformXchg::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(byte_size == sizeof(T)); - STATIC_ASSERT(byte_size == 4 || byte_size == 8); + static_assert(byte_size == sizeof(T)); + static_assert(byte_size == 4 || byte_size == 8); T res = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELEASE); FULL_MEM_BARRIER; return res; @@ -73,7 +73,7 @@ inline T AtomicAccess::PlatformCmpxchg::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(byte_size == sizeof(T)); + static_assert(byte_size == sizeof(T)); if (order == memory_order_conservative) { T value = compare_value; FULL_MEM_BARRIER; @@ -82,7 +82,7 @@ inline T AtomicAccess::PlatformCmpxchg::operator()(T volatile* dest, FULL_MEM_BARRIER; return value; } else { - STATIC_ASSERT ( + static_assert( // The modes that align with C++11 are intended to // follow the same semantics. memory_order_relaxed == __ATOMIC_RELAXED && diff --git a/src/hotspot/os_cpu/bsd_x86/atomicAccess_bsd_x86.hpp b/src/hotspot/os_cpu/bsd_x86/atomicAccess_bsd_x86.hpp index 29471300f3d1..503091423f1a 100644 --- a/src/hotspot/os_cpu/bsd_x86/atomicAccess_bsd_x86.hpp +++ b/src/hotspot/os_cpu/bsd_x86/atomicAccess_bsd_x86.hpp @@ -42,8 +42,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D old_value; __asm__ volatile ( "lock xaddl %0,(%2)" : "=r" (old_value) @@ -60,7 +60,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); __asm__ volatile ( "xchgl (%2),%0" : "=r" (exchange_value) : "0" (exchange_value), "r" (dest) @@ -74,7 +74,7 @@ inline T AtomicAccess::PlatformCmpxchg<1>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(1 == sizeof(T)); + static_assert(1 == sizeof(T)); __asm__ volatile ( "lock cmpxchgb %1,(%3)" : "=a" (exchange_value) : "q" (exchange_value), "a" (compare_value), "r" (dest) @@ -88,7 +88,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); __asm__ volatile ( "lock cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest) @@ -100,8 +100,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D old_value; __asm__ __volatile__ ( "lock xaddq %0,(%2)" : "=r" (old_value) @@ -115,7 +115,7 @@ template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __asm__ __volatile__ ("xchgq (%2),%0" : "=r" (exchange_value) : "0" (exchange_value), "r" (dest) @@ -129,7 +129,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __asm__ __volatile__ ( "lock cmpxchgq %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest) diff --git a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp index 8e45490e5b65..5399f39bf2fa 100644 --- a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp +++ b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp @@ -45,8 +45,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); FULL_MEM_BARRIER; @@ -57,8 +57,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); FULL_MEM_BARRIER; @@ -73,7 +73,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); FULL_MEM_BARRIER; T result = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); FULL_MEM_BARRIER; @@ -85,7 +85,7 @@ template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); FULL_MEM_BARRIER; T result = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); FULL_MEM_BARRIER; @@ -102,7 +102,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T value = compare_value; FULL_MEM_BARRIER; __atomic_compare_exchange(dest, &value, &exchange_value, /*weak*/false, @@ -117,7 +117,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T value = compare_value; FULL_MEM_BARRIER; @@ -137,7 +137,7 @@ inline void atomic_copy64(const volatile void *src, volatile void *dst) { template<> template inline T AtomicAccess::PlatformLoad<8>::operator()(T const volatile* src) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T dest; __atomic_load(const_cast(src), &dest, __ATOMIC_RELAXED); return dest; @@ -147,7 +147,7 @@ template<> template inline void AtomicAccess::PlatformStore<8>::operator()(T volatile* dest, T store_value) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __atomic_store(dest, &store_value, __ATOMIC_RELAXED); } diff --git a/src/hotspot/os_cpu/linux_aarch64/atomicAccess_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/atomicAccess_linux_aarch64.hpp index 4ddb2b758b42..c9b3537fd651 100644 --- a/src/hotspot/os_cpu/linux_aarch64/atomicAccess_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/atomicAccess_linux_aarch64.hpp @@ -85,8 +85,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); aarch64_atomic_stub_t stub; switch (order) { case memory_order_relaxed: @@ -101,8 +101,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); aarch64_atomic_stub_t stub; switch (order) { case memory_order_relaxed: @@ -121,7 +121,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old_value = atomic_fastcall(aarch64_atomic_xchg_4_impl, dest, exchange_value); return old_value; } @@ -130,7 +130,7 @@ template<> template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old_value = atomic_fastcall(aarch64_atomic_xchg_8_impl, dest, exchange_value); return old_value; } @@ -141,7 +141,7 @@ inline T AtomicAccess::PlatformCmpxchg<1>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(1 == sizeof(T)); + static_assert(1 == sizeof(T)); aarch64_atomic_stub_t stub; switch (order) { case memory_order_relaxed: @@ -159,7 +159,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); aarch64_atomic_stub_t stub; switch (order) { case memory_order_relaxed: @@ -182,7 +182,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); aarch64_atomic_stub_t stub; switch (order) { case memory_order_relaxed: diff --git a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp index c03f5ed1c8b3..8fc7583365a2 100644 --- a/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/atomicAccess_linux_arm.hpp @@ -77,7 +77,7 @@ class ARMAtomicFuncs : AllStatic { template<> template inline T AtomicAccess::PlatformLoad<8>::operator()(T const volatile* src) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); return PrimitiveConversions::cast( (*ARMAtomicFuncs::_load_long_func)(reinterpret_cast(src))); } @@ -86,7 +86,7 @@ template<> template inline void AtomicAccess::PlatformStore<8>::operator()(T volatile* dest, T store_value) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); (*ARMAtomicFuncs::_store_long_func)( PrimitiveConversions::cast(store_value), reinterpret_cast(dest)); } @@ -111,8 +111,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); return add_using_helper(ARMAtomicFuncs::_add_func, dest, add_value); } @@ -124,7 +124,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); return xchg_using_helper(ARMAtomicFuncs::_xchg_func, dest, exchange_value); } @@ -164,7 +164,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); return cmpxchg_using_helper(reorder_cmpxchg_func, dest, compare_value, exchange_value); } @@ -174,7 +174,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); return cmpxchg_using_helper(reorder_cmpxchg_long_func, dest, compare_value, exchange_value); } diff --git a/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp index bdbc0b8ac7f8..089a27f15fa3 100644 --- a/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp @@ -47,7 +47,7 @@ struct AtomicAccess::PlatformAdd { #ifndef FULL_COMPILER_ATOMIC_SUPPORT // If we add add and fetch for sub word and are using older compiler // it must be added here due to not using lib atomic. - STATIC_ASSERT(byte_size >= 4); + static_assert(byte_size >= 4); #endif if (order != memory_order_relaxed) { @@ -75,7 +75,7 @@ inline T AtomicAccess::PlatformCmpxchg<1>::operator()(T volatile* dest __attribu T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(1 == sizeof(T)); + static_assert(1 == sizeof(T)); if (order != memory_order_relaxed) { FULL_MEM_BARRIER; @@ -126,7 +126,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest __attribu T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); int32_t old_value; uint64_t rc_temp; @@ -163,11 +163,11 @@ inline T AtomicAccess::PlatformXchg::operator()(T volatile* dest, #ifndef FULL_COMPILER_ATOMIC_SUPPORT // If we add xchg for sub word and are using older compiler // it must be added here due to not using lib atomic. - STATIC_ASSERT(byte_size >= 4); + static_assert(byte_size >= 4); #endif - STATIC_ASSERT(byte_size == sizeof(T)); - STATIC_ASSERT(byte_size == 4 || byte_size == 8); + static_assert(byte_size == sizeof(T)); + static_assert(byte_size == 4 || byte_size == 8); if (order != memory_order_relaxed) { FULL_MEM_BARRIER; @@ -190,10 +190,10 @@ inline T AtomicAccess::PlatformCmpxchg::operator()(T volatile* dest _ atomic_memory_order order) const { #ifndef FULL_COMPILER_ATOMIC_SUPPORT - STATIC_ASSERT(byte_size > 4); + static_assert(byte_size > 4); #endif - STATIC_ASSERT(byte_size == sizeof(T)); + static_assert(byte_size == sizeof(T)); if (order != memory_order_relaxed) { FULL_MEM_BARRIER; } diff --git a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp index 492ccf73bdfc..ccbdab2e80a2 100644 --- a/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp +++ b/src/hotspot/os_cpu/linux_s390/atomicAccess_linux_s390.hpp @@ -85,8 +85,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::add_then_fetch(D volatile* dest, I inc, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D old, upd; @@ -139,8 +139,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::add_then_fetch(D volatile* dest, I inc, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D old, upd; @@ -213,7 +213,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order unused) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old; __asm__ __volatile__ ( @@ -237,7 +237,7 @@ template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order unused) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old; __asm__ __volatile__ ( @@ -295,7 +295,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T cmp_val, T xchg_val, atomic_memory_order unused) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T old; __asm__ __volatile__ ( @@ -319,7 +319,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T cmp_val, T xchg_val, atomic_memory_order unused) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T old; __asm__ __volatile__ ( diff --git a/src/hotspot/os_cpu/linux_x86/atomicAccess_linux_x86.hpp b/src/hotspot/os_cpu/linux_x86/atomicAccess_linux_x86.hpp index 6b43b5e8e099..fe9e8a0273e2 100644 --- a/src/hotspot/os_cpu/linux_x86/atomicAccess_linux_x86.hpp +++ b/src/hotspot/os_cpu/linux_x86/atomicAccess_linux_x86.hpp @@ -42,8 +42,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D old_value; __asm__ volatile ( "lock xaddl %0,(%2)" : "=r" (old_value) @@ -60,7 +60,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); __asm__ volatile ( "xchgl (%2),%0" : "=r" (exchange_value) : "0" (exchange_value), "r" (dest) @@ -74,7 +74,7 @@ inline T AtomicAccess::PlatformCmpxchg<1>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(1 == sizeof(T)); + static_assert(1 == sizeof(T)); __asm__ volatile ("lock cmpxchgb %1,(%3)" : "=a" (exchange_value) : "q" (exchange_value), "a" (compare_value), "r" (dest) @@ -88,7 +88,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); __asm__ volatile ("lock cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest) @@ -100,8 +100,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D old_value; __asm__ __volatile__ ("lock xaddq %0,(%2)" : "=r" (old_value) @@ -114,7 +114,7 @@ template<> template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __asm__ __volatile__ ("xchgq (%2),%0" : "=r" (exchange_value) : "0" (exchange_value), "r" (dest) @@ -128,7 +128,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order /* order */) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __asm__ __volatile__ ("lock cmpxchgq %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest) diff --git a/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp index 96c46c6f59ae..85ab04cb6cb8 100644 --- a/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp +++ b/src/hotspot/os_cpu/linux_zero/atomicAccess_linux_zero.hpp @@ -45,8 +45,8 @@ template<> template inline D AtomicAccess::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(I)); - STATIC_ASSERT(4 == sizeof(D)); + static_assert(4 == sizeof(I)); + static_assert(4 == sizeof(D)); D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); FULL_MEM_BARRIER; @@ -57,8 +57,8 @@ template<> template inline D AtomicAccess::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(I)); - STATIC_ASSERT(8 == sizeof(D)); + static_assert(8 == sizeof(I)); + static_assert(8 == sizeof(D)); D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELEASE); FULL_MEM_BARRIER; @@ -73,7 +73,7 @@ template inline T AtomicAccess::PlatformXchg<4>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); FULL_MEM_BARRIER; T result = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); FULL_MEM_BARRIER; @@ -85,7 +85,7 @@ template inline T AtomicAccess::PlatformXchg<8>::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); FULL_MEM_BARRIER; T result = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); FULL_MEM_BARRIER; @@ -102,7 +102,7 @@ inline T AtomicAccess::PlatformCmpxchg<4>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(4 == sizeof(T)); + static_assert(4 == sizeof(T)); T value = compare_value; FULL_MEM_BARRIER; @@ -118,7 +118,7 @@ inline T AtomicAccess::PlatformCmpxchg<8>::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); FULL_MEM_BARRIER; T value = compare_value; @@ -138,7 +138,7 @@ inline void atomic_copy64(const volatile void *src, volatile void *dst) { template<> template inline T AtomicAccess::PlatformLoad<8>::operator()(T const volatile* src) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); T dest; __atomic_load(const_cast(src), &dest, __ATOMIC_RELAXED); return dest; @@ -148,7 +148,7 @@ template<> template inline void AtomicAccess::PlatformStore<8>::operator()(T volatile* dest, T store_value) const { - STATIC_ASSERT(8 == sizeof(T)); + static_assert(8 == sizeof(T)); __atomic_store(dest, &store_value, __ATOMIC_RELAXED); } diff --git a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp index a92e000950fc..605a8d43345f 100644 --- a/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/atomicAccess_windows_aarch64.hpp @@ -49,7 +49,7 @@ struct AtomicAccess::PlatformAdd { inline D AtomicAccess::PlatformAdd::add_then_fetch(D volatile* dest, \ I add_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(D)); \ + static_assert(sizeof(IntrinsicType) == sizeof(D)); \ IntrinsicType volatile* d = \ reinterpret_cast(dest); \ IntrinsicType inc = \ @@ -86,8 +86,8 @@ struct AtomicAccess::PlatformXchg<1> : AtomicAccess::XchgUsingCmpxchg<1> {}; inline T AtomicAccess::PlatformXchg::operator()(T volatile* dest, \ T exchange_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(T)); \ - STATIC_ASSERT(sizeof(IntrinsicType) == 4 || \ + static_assert(sizeof(IntrinsicType) == sizeof(T)); \ + static_assert(sizeof(IntrinsicType) == 4 || \ sizeof(IntrinsicType) == 8); \ IntrinsicType volatile* d = \ reinterpret_cast(dest); \ @@ -129,7 +129,7 @@ DEFINE_INTRINSIC_XCHG(InterlockedExchange64, __int64) T compare_value, \ T exchange_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(T)); \ + static_assert(sizeof(IntrinsicType) == sizeof(T)); \ IntrinsicType volatile* d = \ reinterpret_cast(dest); \ IntrinsicType xchg = \ diff --git a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp index 252411f62bc6..c9b6303f5dd7 100644 --- a/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp +++ b/src/hotspot/os_cpu/windows_x86/atomicAccess_windows_x86.hpp @@ -59,7 +59,7 @@ struct AtomicAccess::PlatformAdd { inline D AtomicAccess::PlatformAdd::add_then_fetch(D volatile* dest, \ I add_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(D)); \ + static_assert(sizeof(IntrinsicType) == sizeof(D)); \ return PrimitiveConversions::cast( \ IntrinsicName(reinterpret_cast(dest), \ PrimitiveConversions::cast(add_value))); \ @@ -79,8 +79,8 @@ struct AtomicAccess::PlatformXchg<1> : AtomicAccess::XchgUsingCmpxchg<1> {}; inline T AtomicAccess::PlatformXchg::operator()(T volatile* dest, \ T exchange_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(T)); \ - STATIC_ASSERT(sizeof(IntrinsicType) == 4 || \ + static_assert(sizeof(IntrinsicType) == sizeof(T)); \ + static_assert(sizeof(IntrinsicType) == 4 || \ sizeof(IntrinsicType) == 8); \ return PrimitiveConversions::cast( \ IntrinsicName(reinterpret_cast(dest), \ @@ -103,7 +103,7 @@ DEFINE_INTRINSIC_XCHG(InterlockedExchange64, __int64) T compare_value, \ T exchange_value, \ atomic_memory_order order) const { \ - STATIC_ASSERT(sizeof(IntrinsicType) == sizeof(T)); \ + static_assert(sizeof(IntrinsicType) == sizeof(T)); \ return PrimitiveConversions::cast( \ IntrinsicName(reinterpret_cast(dest), \ PrimitiveConversions::cast(exchange_value), \ diff --git a/src/hotspot/share/asm/register.hpp b/src/hotspot/share/asm/register.hpp index 95c7c7922cf3..43405aceb8c3 100644 --- a/src/hotspot/share/asm/register.hpp +++ b/src/hotspot/share/asm/register.hpp @@ -78,7 +78,7 @@ template class ReverseRegSetIterator; template class AbstractRegSet { #ifndef ARM - STATIC_ASSERT(RegImpl::number_of_registers <= 64); + static_assert(RegImpl::number_of_registers <= 64); #endif uint64_t _bitset; diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index fd512807719c..ad8e7cca241e 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -1803,12 +1803,12 @@ Value GraphBuilder::make_constant(ciConstant field_value, ciField* field) { } } -void GraphBuilder::copy_inline_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* enclosing_field) { +void GraphBuilder::copy_value_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* enclosing_field) { for (int i = 0; i < vk->nof_declared_nonstatic_fields(); i++) { ciField* field = vk->declared_nonstatic_field_at(i); int offset = field->offset_in_bytes() - vk->payload_offset(); if (field->is_flat()) { - copy_inline_content(field->type()->as_value_klass(), src, src_off + offset, dest, dest_off + offset, state_before, enclosing_field); + copy_value_content(field->type()->as_value_klass(), src, src_off + offset, dest, dest_off + offset, state_before, enclosing_field); if (!field->is_null_free()) { // Nullable, copy the null marker using Unsafe because null markers are not real fields int null_marker_offset = field->null_marker_offset() - vk->payload_offset(); @@ -2052,7 +2052,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) { NewInstance* buffer = new NewInstance(value_klass, state_before, false, true); _memory->new_instance(buffer); apush(append_split(buffer)); - copy_inline_content(value_klass, pending_field_access()->obj(), + copy_value_content(value_klass, pending_field_access()->obj(), pending_field_access()->offset() + field->offset_in_bytes() - field->holder()->as_value_klass()->payload_offset(), buffer, value_klass->payload_offset(), state_before); set_pending_field_access(nullptr); @@ -2072,7 +2072,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) { // Needs an explicit null check because below code does not perform any actual load if there are no fields null_check(obj); } - copy_inline_content(value_klass, obj, field->offset_in_bytes(), buffer, value_klass->payload_offset(), state_before); + copy_value_content(value_klass, obj, field->offset_in_bytes(), buffer, value_klass->payload_offset(), state_before); Instruction* result = buffer; if (!field->is_null_free()) { @@ -2131,7 +2131,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) { append(new StoreField(obj, offset, field, val, false, state_before, needs_patching)); } else if (field->is_null_free()) { assert(!value_klass->is_empty(), "should have been handled"); - copy_inline_content(value_klass, val, value_klass->payload_offset(), obj, offset, state_before, field); + copy_value_content(value_klass, val, value_klass->payload_offset(), obj, offset, state_before, field); } else { if (!value_klass->is_initialized()) { // null_reset_value is not available, bailout for now @@ -2143,7 +2143,7 @@ void GraphBuilder::access_field(Bytecodes::Code code) { Value object_null = append(new Constant(objectNull)); Value null_reset_value = append(new Constant(new ObjectConstant(value_klass->get_null_reset_value().as_object()))); Value src = append(new IfOp(val, Instruction::neq, object_null, val, null_reset_value, state_before, false)); - copy_inline_content(value_klass, src, value_klass->payload_offset(), obj, offset, state_before); + copy_value_content(value_klass, src, value_klass->payload_offset(), obj, offset, state_before); // Store the null marker Value int_one = append(new Constant(new IntConstant(1))); diff --git a/src/hotspot/share/c1/c1_GraphBuilder.hpp b/src/hotspot/share/c1/c1_GraphBuilder.hpp index 9251f496db65..0a4f31983508 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.hpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.hpp @@ -299,7 +299,7 @@ class GraphBuilder { void throw_op(int bci); // value types - void copy_inline_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* enclosing_field = nullptr); + void copy_value_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off, ValueStack* state_before, ciField* enclosing_field = nullptr); // stack/code manipulation helpers Instruction* append_with_bci(Instruction* instr, int bci); diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index 90e8f103e8aa..e39ba7763211 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -657,15 +657,15 @@ void LIRGenerator::print_if_not_loaded(const NewInstance* new_instance) { } #endif -void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_inline, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info) { - if (allow_inline) { +void LIRGenerator::new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_value, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info) { + if (allow_value) { assert(!is_unresolved && klass->is_loaded(), "value type klass should be resolved"); __ metadata2reg(klass->constant_encoding(), klass_reg); } else { klass2reg_with_patching(klass_reg, klass, info, is_unresolved); } // If klass is not loaded we do not know if the klass has finalizers or is an unexpected value klass - if (UseFastNewInstance && klass->is_loaded() && (allow_inline || !klass->is_value_klass()) + if (UseFastNewInstance && klass->is_loaded() && (allow_value || !klass->is_value_klass()) && !Klass::layout_helper_needs_slow_path(klass->layout_helper())) { StubId stub_id = klass->is_initialized() ? StubId::c1_fast_new_instance_id : StubId::c1_fast_new_instance_init_check_id; diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index 4cd09678c572..6a4df6543b3f 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -380,7 +380,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void monitor_enter (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no, CodeEmitInfo* info_for_exception, CodeEmitInfo* info, CodeStub* throw_ie_stub = nullptr); void monitor_exit (LIR_Opr object, LIR_Opr lock, LIR_Opr hdr, LIR_Opr scratch, int monitor_no); - void new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_inline, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info); + void new_instance(LIR_Opr dst, ciInstanceKlass* klass, bool is_unresolved, bool allow_value, LIR_Opr scratch1, LIR_Opr scratch2, LIR_Opr scratch3, LIR_Opr scratch4, LIR_Opr klass_reg, CodeEmitInfo* info); // machine dependent void cmp_mem_int(LIR_Condition condition, LIR_Opr base, int disp, int c, CodeEmitInfo* info); diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 1c274e958bfc..30476c96109c 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -268,6 +268,13 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { // The following are not appliable to unregistered classes return; } + ConstantPoolCache* cpCache = ik->constants()->cache(); + if (cpCache != nullptr) { + // Resolved{Field,Indy,Method}Entries in cpCache are not walked by MetaspaceClosure, + // because only some of the resolved entries are archivable. The following call will + // discover the anonymous classes that are reachable only from the archivable entries. + cpCache->record_classes_in_archivable_entries(); + } scan_oops_in_instance_class(ik); if (ik->is_hidden() && CDSConfig::is_dumping_aot_linked_classes()) { bool succeed = AOTClassLinker::try_add_candidate(ik); diff --git a/src/hotspot/share/cds/aotLogging.hpp b/src/hotspot/share/cds/aotLogging.hpp index f9515341d72c..9c0be076c53f 100644 --- a/src/hotspot/share/cds/aotLogging.hpp +++ b/src/hotspot/share/cds/aotLogging.hpp @@ -122,7 +122,7 @@ class AOTLogImpl { // Make sure no more than the maximum number of tags have been given. // The GuardTag allows this to be detected if/when it happens. If the GuardTag // is not __NO_TAG, the number of tags given exceeds the maximum allowed. - STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported! + static_assert(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported! // Empty constructor to avoid warnings on MSVC about unused variables // when the log instance is only used for static functions. diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index 2e3cbd24a65e..74dfcb0b77f8 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -579,6 +579,7 @@ address AOTMappedHeapLoader::heap_region_requested_address(FileMapInfo* info) { } } +#if INCLUDE_G1GC bool AOTMappedHeapLoader::map_heap_region(FileMapInfo* info) { if (map_heap_region_impl(info)) { #ifdef ASSERT @@ -694,6 +695,8 @@ bool AOTMappedHeapLoader::map_heap_region_impl(FileMapInfo* info) { return true; } +#endif // INCLUDE_G1GC + narrowOop AOTMappedHeapLoader::encoded_heap_region_dumptime_address(FileMapInfo* info) { assert(CDSConfig::is_using_archive(), "runtime only"); assert(UseCompressedOops, "sanity"); @@ -716,6 +719,7 @@ void AOTMappedHeapLoader::patch_heap_embedded_pointers(FileMapInfo* info) { r->oopmap_size_in_bits()); } +#if INCLUDE_G1GC void AOTMappedHeapLoader::fixup_mapped_heap_region(FileMapInfo* info) { if (is_mapped()) { assert(!_mapped_heap_memregion.is_empty(), "sanity"); @@ -732,6 +736,7 @@ void AOTMappedHeapLoader::fixup_mapped_heap_region(FileMapInfo* info) { void AOTMappedHeapLoader::dealloc_heap_region(FileMapInfo* info) { G1CollectedHeap::heap()->dealloc_archive_regions(_mapped_heap_memregion); } +#endif // INCLUDE_G1GC AOTMapLogger::OopDataIterator* AOTMappedHeapLoader::oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end) { class MappedLoaderOopIterator : public AOTMappedHeapOopIterator { diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.hpp b/src/hotspot/share/cds/aotMappedHeapLoader.hpp index b0fa18cf5738..3f7735dc2813 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.hpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.hpp @@ -170,12 +170,12 @@ class AOTMappedHeapLoader : AllStatic { static bool map_heap_region_impl(FileMapInfo* info); static narrowOop encoded_heap_region_dumptime_address(FileMapInfo* info); static void patch_heap_embedded_pointers(FileMapInfo* info); - static void fixup_mapped_heap_region(FileMapInfo* info); + static void fixup_mapped_heap_region(FileMapInfo* info) NOT_G1GC_RETURN; static void dealloc_heap_region(FileMapInfo* info); public: - static bool map_heap_region(FileMapInfo* info); + static bool map_heap_region(FileMapInfo* info) NOT_G1GC_RETURN_(false); static bool load_heap_region(FileMapInfo* mapinfo); static void assert_in_loaded_heap(uintptr_t o) { assert(is_in_loaded_heap(o), "must be"); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 4ffc44c6eae0..164cd2e7ee03 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -98,7 +98,7 @@ void AOTMappedHeapWriter::init() { _native_pointers = new GrowableArrayCHeap(2048); _source_objs = new GrowableArrayCHeap(10000); - guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be"); + G1GC_ONLY(guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be");) if (CDSConfig::old_cds_flags_used()) { // With the old CDS workflow, we can guatantee determninistic output: given @@ -624,6 +624,7 @@ void AOTMappedHeapWriter::set_requested_address_range(AOTMappedHeapInfo* info) { AOTMetaspace::unrecoverable_writing_error(); } _requested_bottom = align_down(heap_end - heap_region_byte_size, alignment); +#if INCLUDE_G1GC } else if (UseG1GC) { // For G1, pick the range at the top of the current heap. If the exact same heap sizes // are used in the production run, it's likely that we can map the archived objects @@ -633,6 +634,7 @@ void AOTMappedHeapWriter::set_requested_address_range(AOTMappedHeapInfo* info) { _requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes); _requested_bottom = align_down(_requested_bottom, MIN_GC_REGION_ALIGNMENT); assert(is_aligned(_requested_bottom, G1HeapRegion::GrainBytes), "sanity"); +#endif } else { _requested_bottom = align_up(CompressedOops::begin(), MIN_GC_REGION_ALIGNMENT); } diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index 64b6c27e78f6..30d976a0fe15 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -145,6 +145,7 @@ void AOTStreamedHeapWriter::order_source_objs(GrowableArrayCHeapget(cast_from_oop(obj)); + assert(dfs_number != nullptr, "reachable obj not added to _source_objs"); if (*dfs_number != -1) { // Already visited in the traversal continue; diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 76f133d2f419..8ada43de20cb 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1670,8 +1670,12 @@ bool FileMapInfo::can_use_heap_region() { narrow_oop_mode(), p2i(narrow_oop_base()), narrow_oop_shift()); aot_log_info(aot)(" AOTCompatibleOopCompression = %s", header()->compatible_oop_compression() ? "true" : "false"); } +#if INCLUDE_G1GC aot_log_info(aot)("The current max heap size = %zuM, G1HeapRegion::GrainBytes = %zu", MaxHeapSize/M, G1HeapRegion::GrainBytes); +#else + aot_log_info(aot)("The current max heap size = %zuM", MaxHeapSize/M); +#endif aot_log_info(aot)(" narrow_klass_base = " PTR_FORMAT ", arrow_klass_pointer_bits = %d, narrow_klass_shift = %d", p2i(CompressedKlassPointers::base()), CompressedKlassPointers::narrow_klass_pointer_bits(), CompressedKlassPointers::shift()); if (UseCompressedOops) { @@ -1682,9 +1686,9 @@ bool FileMapInfo::can_use_heap_region() { if (!object_streaming_mode()) { aot_log_info(aot)(" heap range = [" PTR_FORMAT " - " PTR_FORMAT "]", UseCompressedOops ? p2i(CompressedOops::begin()) : - UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().start()) : 0L, + G1GC_ONLY(UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().start()) :) 0L, UseCompressedOops ? p2i(CompressedOops::end()) : - UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().end()) : 0L); + G1GC_ONLY(UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().end()) :) 0L); } int err = 0; diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 2cd96ce718fd..185a18d96595 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -422,6 +422,22 @@ void HeapShared::archive_interned_string(oop string) { assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); } +void HeapShared::update_scratch_mirror_field(oop scratch_m, int field_offset, oop new_field_obj) { + archive_updated_mirror_field(scratch_m->obj_field(field_offset), new_field_obj); + scratch_m->obj_field_put(field_offset, new_field_obj); +} + +// It's possible for a scratch mirror to be scanned before one of its oop fields (such as acmp_maps) is +// updated. When this happens, we must ensure that the new oop is also archived. +void HeapShared::archive_updated_mirror_field(oop old_field_obj, oop new_field_obj) { + if (old_field_obj != new_field_obj) { + assert(old_field_obj == nullptr, "cannot update a non-null oop field that might have already been scanned"); + assert(new_field_obj != nullptr, "must be"); + bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, new_field_obj); + assert(success, "mirror field must be archivable"); + } +} + void HeapShared::finalize_initialization(FileMapInfo* static_mapinfo) { if (HeapShared::is_loading()) { if (HeapShared::is_loading_streaming_mode()) { @@ -812,7 +828,6 @@ bool HeapShared::is_archivable_hidden_klass(InstanceKlass* ik) { (is_lambda_form_klass(ik) || is_lambda_proxy_klass(ik) || is_string_concat_klass(ik)); } - void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { ik->set_has_aot_initialized_mirror(); @@ -843,11 +858,7 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { // to null and it will be recreated at runtime. field_obj = nullptr; } - m->obj_field_put(offset, field_obj); - if (field_obj != nullptr) { - bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, field_obj); - assert(success, "sanity"); - } + update_scratch_mirror_field(m, offset, field_obj); } break; case T_BOOLEAN: @@ -882,19 +893,14 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { } oop class_data = java_lang_Class::class_data(orig_mirror); + archive_updated_mirror_field(java_lang_Class::class_data(m), class_data); java_lang_Class::set_class_data(m, class_data); - if (class_data != nullptr) { - bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, class_data); - assert(success, "sanity"); - } if (ik->is_value_klass()) { ValueKlass* vk = ValueKlass::cast(ik); if (vk->supports_nullable_layouts()) { oop null_reset_value = vk->null_reset_value(); - m->obj_field_put(vk->null_reset_value_offset(), null_reset_value); - bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, null_reset_value); - assert(success, "sanity"); + update_scratch_mirror_field(m, vk->null_reset_value_offset(), null_reset_value); } } @@ -930,14 +936,18 @@ void HeapShared::copy_java_mirror(oop orig_mirror, oop scratch_m) { if (ik->has_acmp_maps_offset()) { int maps_offset = ik->acmp_maps_offset(); - oop maps = orig_mirror->obj_field(maps_offset); - scratch_m->obj_field_put(maps_offset, maps); + update_scratch_mirror_field(scratch_m, maps_offset, orig_mirror->obj_field(maps_offset)); } } if (CDSConfig::is_dumping_aot_linked_classes()) { - java_lang_Class::set_module(scratch_m, java_lang_Class::module(orig_mirror)); - java_lang_Class::set_protection_domain(scratch_m, java_lang_Class::protection_domain(orig_mirror)); + oop module = java_lang_Class::module(orig_mirror); + archive_updated_mirror_field(java_lang_Class::module(scratch_m), module); + java_lang_Class::set_module(scratch_m, module); + + oop pd = java_lang_Class::protection_domain(orig_mirror); + archive_updated_mirror_field(java_lang_Class::protection_domain(scratch_m), pd); + java_lang_Class::set_protection_domain(scratch_m, pd); } } @@ -988,9 +998,9 @@ void HeapShared::start_scanning_for_oops() { if (HeapShared::is_writing_mapping_mode() && (UseG1GC || UseCompressedOops)) { aot_log_info(aot)("Heap range = [" PTR_FORMAT " - " PTR_FORMAT "]", UseCompressedOops ? p2i(CompressedOops::begin()) : - p2i((address)G1CollectedHeap::heap()->reserved().start()), + G1GC_ONLY(UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().start()) :) 0L, UseCompressedOops ? p2i(CompressedOops::end()) : - p2i((address)G1CollectedHeap::heap()->reserved().end())); + G1GC_ONLY(UseG1GC ? p2i((address)G1CollectedHeap::heap()->reserved().end()) :) 0L); } archive_subgraphs(); @@ -1759,7 +1769,7 @@ class HeapShared::ValueKlassFinder : public FieldClosure { ValueKlassFinder(KlassSubGraphInfo* subgraph_info, InstanceKlass* ik, address obj) : _subgraph_info(subgraph_info), _ik(ik), _obj(obj) { precond(obj != nullptr); - precond(ik->has_inlined_fields()); + precond(ik->has_flat_fields()); } // This function is called on every field of _ik. @@ -1777,7 +1787,7 @@ class HeapShared::ValueKlassFinder : public FieldClosure { if (fd->is_null_free_value_type() || !vk->is_payload_marked_as_null(field_addr)) { // Found a non-null flattened instance of vk. Let's record vk. add_value_class(_subgraph_info, vk); - if (vk->has_inlined_fields()) { + if (vk->has_flat_fields()) { ValueKlassFinder finder(_subgraph_info, vk, field_addr); finder.find(); } @@ -1827,7 +1837,7 @@ void HeapShared::find_value_classes(KlassSubGraphInfo* subgraph_info, oop orig_o if (!added) { add_value_class(subgraph_info, elem_k); } - if (elem_k->has_inlined_fields()) { + if (elem_k->has_flat_fields()) { // "logical address" of the i-th array element. address elem = static_cast
(fa->value_at_addr(i, fak->layout_helper())) - elem_k->payload_offset(); ValueKlassFinder finder(subgraph_info, elem_k, elem); @@ -1837,7 +1847,7 @@ void HeapShared::find_value_classes(KlassSubGraphInfo* subgraph_info, oop orig_o } } else if (klass->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(klass); - if (ik->has_inlined_fields()) { + if (ik->has_flat_fields()) { ValueKlassFinder finder(subgraph_info, ik, cast_from_oop
(orig_obj)); finder.find(); } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index a293abbe8880..bb91b21d3400 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -163,6 +163,8 @@ class HeapShared: AllStatic { static void enable_gc() NOT_CDS_JAVA_HEAP_RETURN; static void materialize_thread_object() NOT_CDS_JAVA_HEAP_RETURN; static void archive_interned_string(oop string); + static void update_scratch_mirror_field(oop scratch_m, int field_offset, oop obj); + static void archive_updated_mirror_field(oop old_field_obj, oop new_field_obj); static void finalize_initialization(FileMapInfo* static_mapinfo) NOT_CDS_JAVA_HEAP_RETURN; private: diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp index c4c516002dd3..a25d1257b177 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,13 +38,21 @@ #ifndef PRODUCT #define TRACE_BCEA(level, code) \ - if (EstimateArgEscape && BCEATraceLevel >= level) { \ + if (EstimateArgEscape && BCEATraceLevel >= level) { \ + print_header(_level); \ code; \ } #else #define TRACE_BCEA(level, code) #endif +static const int INDENTATION_WIDTH = 2; + +static void print_header(int level) { + tty->print("[EA] "); + tty->sp(INDENTATION_WIDTH * clamp(level, 0, max_jint / INDENTATION_WIDTH)); +} + // Maintain a map of which arguments a local variable or // stack slot may contain. In addition to tracking // arguments, it tracks two special values, "allocated" @@ -298,7 +306,7 @@ void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod* skip_callee = true; } if (skip_callee) { - TRACE_BCEA(3, tty->print_cr("[EA] skipping method %s::%s", holder->name()->as_utf8(), target->name()->as_utf8())); + TRACE_BCEA(3, tty->print_cr("skipping method %s::%s", holder->name()->as_klass_external_name(), target->name()->as_utf8())); for (i = 0; i < arg_size; i++) { set_method_escape(state.raw_pop()); } @@ -360,7 +368,7 @@ void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod* _dependencies.appendAll(analyzer.dependencies()); } } else { - TRACE_BCEA(1, tty->print_cr("[EA] virtual method %s is not monomorphic.", + TRACE_BCEA(1, tty->print_cr("virtual method %s is not monomorphic.", target->name()->as_utf8())); // conservatively mark all actual parameters as escaping globally for (i = 0; i < arg_size; i++) { @@ -1326,7 +1334,8 @@ void BCEscapeAnalyzer::compute_escape_info() { || _level > MaxBCEAEstimateLevel || method()->code_size() > MaxBCEAEstimateSize)) { if (BCEATraceLevel >= 1) { - tty->print("Skipping method because: "); + print_header(level()); + tty->print("skipping method because: "); if (method()->is_abstract()) tty->print_cr("method is abstract."); else if (method()->is_native()) @@ -1348,7 +1357,8 @@ void BCEscapeAnalyzer::compute_escape_info() { } if (BCEATraceLevel >= 1) { - tty->print("[EA] estimating escape information for"); + print_header(level()); + tty->print("estimating escape information for"); if (iid != vmIntrinsics::_none) tty->print(" intrinsic"); method()->print_short_name(); @@ -1429,38 +1439,75 @@ void BCEscapeAnalyzer::read_escape_info() { } #ifndef PRODUCT + +static const char* const COMMA_SEPARATOR = ", "; + +void BCEscapeAnalyzer::dump_arg_set(const VectorSet &set) { + tty->print("{"); + const char* sep = ""; + for (int i = 0; i < _arg_size; i++) { + if (set.test(i)) { + tty->print("%s%d", sep, i); + sep = COMMA_SEPARATOR; + } + } + tty->print("}"); +} + void BCEscapeAnalyzer::dump() { - tty->print("[EA] estimated escape information for"); + print_header(level()); + tty->print("estimated escape information for"); method()->print_short_name(); - tty->print_cr(has_dependencies() ? " (not stored)" : ""); - tty->print(" non-escaping args: "); - _arg_local.print(); - tty->print(" stack-allocatable args: "); - _arg_stack.print(); + tty->print(has_dependencies() ? " (not stored)" : ""); + tty->print_cr(_arg_size == 1 ? " (%d arg slot):" : " (%d arg slots):", _arg_size); + print_header(level()); + tty->print("- non-escaping args: "); + dump_arg_set(_arg_local); + tty->cr(); + print_header(level()); + tty->print("- stack-allocatable args: "); + dump_arg_set(_arg_stack); + tty->cr(); + print_header(level()); if (_return_local) { - tty->print(" returned args: "); - _arg_returned.print(); + tty->print("- returned args: "); + dump_arg_set(_arg_returned); + tty->cr(); } else if (is_return_allocated()) { - tty->print_cr(" return allocated value"); + tty->print_cr("- return allocated value"); } else { - tty->print_cr(" return non-local value"); + tty->print_cr("- return non-local value"); } - tty->print(" modified args: "); + print_header(level()); + tty->print("- modified args: "); + tty->print("["); + const char* sep = ""; for (int i = 0; i < _arg_size; i++) { - if (_arg_modified[i] == 0) - tty->print(" 0"); - else - tty->print(" 0x%x", _arg_modified[i]); + tty->print("%s", sep); + if (_arg_modified[i] == 0) { + tty->print("0"); + } else { + tty->print("0x%x", _arg_modified[i]); + } + sep = COMMA_SEPARATOR; } - tty->cr(); - tty->print(" flags: "); - if (_return_allocated) - tty->print(" return_allocated"); - if (_allocated_escapes) - tty->print(" allocated_escapes"); - if (_unknown_modified) - tty->print(" unknown_modified"); - tty->cr(); + tty->print_cr("]"); + print_header(level()); + tty->print("- flags: {"); + sep = ""; + if (_return_allocated) { + tty->print("%sreturn_allocated", sep); + sep = COMMA_SEPARATOR; + } + if (_allocated_escapes) { + tty->print("%sallocated_escapes", sep); + sep = COMMA_SEPARATOR; + } + if (_unknown_modified) { + tty->print("%sunknown_modified", sep); + sep = COMMA_SEPARATOR; + } + tty->print_cr("}"); } #endif @@ -1491,15 +1538,14 @@ BCEscapeAnalyzer::BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent) if (methodData() == nullptr) return; if (methodData()->has_escape_info()) { - TRACE_BCEA(2, tty->print_cr("[EA] Reading previous results for %s.%s", - method->holder()->name()->as_utf8(), + TRACE_BCEA(2, tty->print_cr("reading previous results for %s::%s", + method->holder()->name()->as_klass_external_name(), method->name()->as_utf8())); read_escape_info(); } else { - TRACE_BCEA(2, tty->print_cr("[EA] computing results for %s.%s", - method->holder()->name()->as_utf8(), + TRACE_BCEA(2, tty->print_cr("computing results for %s::%s", + method->holder()->name()->as_klass_external_name(), method->name()->as_utf8())); - compute_escape_info(); methodData()->update_escape_info(); } diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp index e7e24a067fe7..86615ef5595c 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -160,6 +160,8 @@ class BCEscapeAnalyzer : public ArenaObj { static bool datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize); #ifndef PRODUCT + // Dump indices within an argument set + void dump_arg_set(const VectorSet &set); // dump escape information void dump(); #endif diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 299e3b2d5fbd..1e33abd993f4 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -240,7 +240,7 @@ ciField::ciField(ciField* declared_field, ciField* subfield) { _signature = subfield->_signature; _type = subfield->_type; - _is_constant = (declared_field->is_strict() && declared_field->is_final()) || declared_field->is_constant(); + _is_constant = declared_field->is_constant(); _known_to_link_with_put = subfield->_known_to_link_with_put; _known_to_link_with_get = subfield->_known_to_link_with_get; _constant_value = ciConstant(); @@ -269,7 +269,7 @@ ciField::ciField(ciField* declared_field) { _signature = ciSymbols::bool_signature(); _type = ciType::make(T_BOOLEAN); - _is_constant = (declared_field->is_strict() && declared_field->is_final()) || declared_field->is_constant(); + _is_constant = declared_field->is_constant(); _known_to_link_with_put = nullptr; _known_to_link_with_get = nullptr; _constant_value = ciConstant(); @@ -299,9 +299,6 @@ static bool trust_final_nonstatic_fields(ciInstanceKlass* holder) { // can't be serialized, so there is no hacking of finals going on with them. if (holder->is_hidden()) return true; - // Trust final fields in value type buffers - if (holder->is_value_klass()) - return true; // Trust final fields in records if (holder->is_record()) return true; @@ -337,10 +334,10 @@ void ciField::initialize_from(fieldDescriptor* fd) { // java.lang.System.out, and java.lang.System.err. _is_constant = !fd->is_mutable_static_final(); } else { - // An instance field can be constant if it's a final static field or if - // it's a final non-static field of a trusted class (classes in - // java.lang.invoke and sun.invoke packages and subpackages). - _is_constant = is_stable_field || trust_final_nonstatic_fields(_holder); + // A final field should generally be constant, but reflection is allowed to subvert this + // expection, so we only consider a final field constant if either it is strict, or it is one + // of some special cases where we want constant folding + _is_constant = is_strict() || is_stable_field || trust_final_nonstatic_fields(_holder); } } else { // For CallSite objects treat the target field as a compile time constant. diff --git a/src/hotspot/share/ci/ciMethodData.cpp b/src/hotspot/share/ci/ciMethodData.cpp index fe1cce9dadee..382e7e70a684 100644 --- a/src/hotspot/share/ci/ciMethodData.cpp +++ b/src/hotspot/share/ci/ciMethodData.cpp @@ -240,7 +240,7 @@ bool ciMethodData::load_data() { #ifndef ZERO // Some Zero platforms do not have expected alignment, and do not use // this code. static_assert would still fire and fail for them. - static_assert(sizeof(_orig) % HeapWordSize == 0, "align"); + static_assert(sizeof(_orig) % HeapWordSize == 0); #endif Copy::disjoint_words_atomic((HeapWord*) &mdo->_compiler_counters, (HeapWord*) &_orig, diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index b689f13b8d7a..7c7fdd99a1e0 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -6418,10 +6418,10 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st _must_be_atomic, _layout_info, _value_field_layout_info_array); lb.build_layout(); - // If it turned out that we didn't inline any of the fields, we deallocate + // If it turned out that we didn't flatten any of the fields, we deallocate // the array of ValueFieldLayoutInfo since it isn't needed, and so it isn't // transferred to the allocated InstanceKlass. - if (_value_field_layout_info_array != nullptr && !(_layout_info->_has_inlined_fields || _has_null_restricted_static_fields)) { + if (_value_field_layout_info_array != nullptr && !(_layout_info->_has_flat_fields || _has_null_restricted_static_fields)) { MetadataFactory::free_array(_loader_data, _value_field_layout_info_array); _value_field_layout_info_array = nullptr; } diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 945d965fe7a5..4ac29b4b7d8d 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -98,7 +98,7 @@ class FieldLayoutInfo : public ResourceObj { bool _has_nonstatic_fields; bool _is_naturally_atomic; bool _must_be_atomic; - bool _has_inlined_fields; + bool _has_flat_fields; bool _is_empty_value_klass; FieldLayoutInfo() : oop_map_blocks(nullptr), _nonoop_acmp_map(nullptr), _oop_acmp_map(nullptr), _instance_size(-1), _nonstatic_field_size(-1), _static_field_size(-1), @@ -108,7 +108,7 @@ class FieldLayoutInfo : public ResourceObj { _nullable_non_atomic_layout_size_in_bytes(-1), _null_marker_offset(-1), _null_reset_value_offset(-1), _acmp_maps_offset(-1), _has_nonstatic_fields(false), _is_naturally_atomic(false), _must_be_atomic(false), - _has_inlined_fields(false), _is_empty_value_klass(false) { } + _has_flat_fields(false), _is_empty_value_klass(false) { } }; // Parser for for .class files @@ -577,7 +577,7 @@ class ClassFileParser { bool is_interface() const { return _access_flags.is_interface(); } bool is_concrete_value_class() const { return !_access_flags.is_identity_class() && !_access_flags.is_interface() && !_access_flags.is_abstract(); } bool is_identity_class() const { return _access_flags.is_identity_class(); } - bool has_inlined_fields() const { return _layout_info->_has_inlined_fields; } + bool has_flat_fields() const { return _layout_info->_has_flat_fields; } u2 java_fields_count() const { return _java_fields_count; } bool is_abstract() const { return _access_flags.is_abstract(); } diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index 7bdd4ea2c59f..c44a72b55c51 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -119,9 +119,9 @@ static LayoutKind adjust_with_budget(FieldInfo field_info, Array* li) { +static bool field_is_flattenable(FieldInfo fieldinfo, LayoutKind lk, Array* li) { if (fieldinfo.field_flags().is_null_free_value_type()) { - // A null-free value type is always inlineable + // A null-free value type is always flattenable return true; } @@ -808,8 +808,8 @@ FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* _is_naturally_atomic(false), _must_be_atomic(must_be_atomic), _has_nonstatic_fields(false), - _has_inlineable_fields(false), - _has_inlined_fields(false), + _has_flattenable_fields(false), + _has_flat_fields(false), _is_contended(is_contended), _is_concrete_value(is_concrete_value), _is_abstract_value(is_abstract_value), @@ -863,8 +863,8 @@ int FieldLayoutBuilder::add_field_to_group(FieldInfo fieldinfo, int idx, FieldGr const bool use_atomic_flat = !is_value_compatible_class || _must_be_atomic; LayoutKind lk = field_layout_selection(fieldinfo, _value_field_layout_info_array, use_atomic_flat); lk = adjust_with_budget(fieldinfo, _value_field_layout_info_array, lk, _flattening_budget); - if (field_is_inlineable(fieldinfo, lk, _value_field_layout_info_array)) { - _has_inlineable_fields = true; + if (field_is_flattenable(fieldinfo, lk, _value_field_layout_info_array)) { + _has_flattenable_fields = true; } if (lk == LayoutKind::REFERENCE) { @@ -882,7 +882,7 @@ int FieldLayoutBuilder::add_field_to_group(FieldInfo fieldinfo, int idx, FieldGr const int field_index = (int)fieldinfo.index(); assert(_value_field_layout_info_array != nullptr, "Array must have been created"); assert(_value_field_layout_info_array->adr_at(field_index)->klass() != nullptr, "Klass must have been set"); - _has_inlined_fields = true; + _has_flat_fields = true; ValueKlass* vk = _value_field_layout_info_array->adr_at(field_index)->klass(); if (is_value_compatible_class && !vk->is_naturally_atomic(LayoutKindHelper::is_null_free_flat(lk))) { _has_non_naturally_atomic_fields = true; @@ -1533,7 +1533,7 @@ void FieldLayoutBuilder::epilogue() { _info->_static_field_size = static_fields_size; _info->_nonstatic_field_size = (nonstatic_field_end - instanceOopDesc::base_offset_in_bytes()) / heapOopSize; _info->_has_nonstatic_fields = _has_nonstatic_fields; - _info->_has_inlined_fields = _has_inlined_fields; + _info->_has_flat_fields = _has_flat_fields; _info->_is_naturally_atomic = _is_naturally_atomic; if (_is_concrete_value) { _info->_must_be_atomic = _must_be_atomic; @@ -1589,7 +1589,7 @@ void FieldLayoutBuilder::epilogue() { static bool first_layout_print = true; - if (PrintFieldLayout || (PrintValueLayout && (_has_inlineable_fields || _is_concrete_value || _is_abstract_value))) { + if (PrintFieldLayout || (PrintValueLayout && (_has_flattenable_fields || _is_concrete_value || _is_abstract_value))) { ResourceMark rm; stringStream st; if (first_layout_print) { diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index 985123fca746..8bd33b43db5a 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -303,8 +303,8 @@ class FieldLayoutBuilder : public ResourceObj { bool _is_naturally_atomic; bool _must_be_atomic; bool _has_nonstatic_fields; - bool _has_inlineable_fields; - bool _has_inlined_fields; + bool _has_flattenable_fields; + bool _has_flat_fields; bool _is_contended; bool _super_ends_with_oop; bool _is_concrete_value; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f27473eeddc4..d5fc89f9cd0a 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1210,7 +1210,7 @@ InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik, return nullptr; } - if (ik->has_inlined_fields() || ik->has_null_restricted_static_fields()) { + if (ik->has_flat_fields() || ik->has_null_restricted_static_fields()) { for (AllFieldStream fs(ik); !fs.done(); fs.next()) { if (fs.access_flags().is_static() && !fs.is_null_free_value_type()) { continue; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index a3258f433dda..ea9747f72406 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -300,10 +300,10 @@ class SystemDictionaryShared::ExclusionCheckCandidates }); } - // Inline fields need to have their layouts preserved between dumptime and runtime. + // Value fields need to have their layouts preserved between dumptime and runtime. // To ensure this, the types of the fields must be stored in the archive along with // the field holder. - if (k->has_inlined_fields() || k->has_null_restricted_static_fields()) { + if (k->has_flat_fields() || k->has_null_restricted_static_fields()) { for (AllFieldStream fs(k); !fs.done(); fs.next()) { if (fs.is_flat() || fs.is_null_free_value_type()) { ValueKlass* field_klass = k->get_value_type_field_klass(fs.index()); @@ -536,7 +536,7 @@ bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, Dump // If any of the null restricted or flat field types are excluded, the current // klass must be excluded as well, otherwise there is no guarantee that the // field layouts will be consistent at runtime. - if (k->has_inlined_fields() || k->has_null_restricted_static_fields()) { + if (k->has_flat_fields() || k->has_null_restricted_static_fields()) { for (AllFieldStream fs(k); !fs.done(); fs.next()) { if (fs.is_flat() || fs.is_null_free_value_type()) { ValueKlass* field_klass = k->get_value_type_field_klass(fs.index()); diff --git a/src/hotspot/share/classfile/verificationType.hpp b/src/hotspot/share/classfile/verificationType.hpp index 7aeec3efdc3a..9af139056fde 100644 --- a/src/hotspot/share/classfile/verificationType.hpp +++ b/src/hotspot/share/classfile/verificationType.hpp @@ -68,7 +68,7 @@ class VerificationType { // Enum for the _data field enum : uint { - // Bottom three bits determine if the type is a reference, value type, + // Bottom three bits determine if the type is a reference, // primitive, uninitialized or a query-type. TypeMask = 0x00000007, diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 63e4d7bd857a..ff2fc02c5c9a 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -2736,7 +2736,7 @@ void ClassVerifier::verify_invoke_instructions( // or any superclass (including Object). else if (opcode == Bytecodes::_invokespecial && !is_same_or_direct_interface(current_class(), current_type(), ref_class_type) - && !ref_class_type.equals(VerificationType::reference_type(current_class()->super()->name()))) { // super() can never be a value_type. + && !ref_class_type.equals(VerificationType::reference_type(current_class()->super()->name()))) { // We know it is not current class, direct superinterface or immediate superclass. That means it // could be: diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index e97f522b9ab4..0ea298521d20 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -2112,9 +2112,9 @@ void AOTCodeAddressTable::init_extrs() { 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 +#endif ADD_EXTERNAL_ADDRESS(BarrierSetNMethod::nmethod_stub_entry_barrier); // used by method_entry_barrier -#endif #if INCLUDE_SHENANDOAHGC ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre); ADD_EXTERNAL_ADDRESS(ShenandoahRuntime::write_barrier_pre_narrow); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 5a4457f567a4..a671faa72394 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -2038,6 +2038,9 @@ void nmethod::finalize_relocations() { next_data++; } } + + BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); + bs_nm->finalize_relocations(this); } void nmethod::make_deoptimized() { @@ -3885,6 +3888,13 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { st.print("barrier format=%d", reloc->format()); return st.as_string(); } + case relocInfo::patchable_barrier_type: { + patchable_barrier_Relocation* const reloc = iter.patchable_barrier_reloc(); + stringStream st; + st.print("patchable_barrier metadata=0x%x target=" PTR_FORMAT, + reloc->metadata(), (intptr_t)code_begin() + reloc->target_offset()); + return st.as_string(); + } case relocInfo::type_mask: return "type_bit_mask"; diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 5295dc0f2873..1d17f40b0c51 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -279,7 +279,7 @@ Relocation* RelocIterator::reloc() { // Verify all the destructors are trivial, so we don't need to worry about // destroying old contents of a RelocationHolder being assigned or destroyed. #define VERIFY_TRIVIALLY_DESTRUCTIBLE_AUX(Reloc) \ - static_assert(std::is_trivially_destructible::value, "must be"); + static_assert(std::is_trivially_destructible::value); #define VERIFY_TRIVIALLY_DESTRUCTIBLE(name) \ VERIFY_TRIVIALLY_DESTRUCTIBLE_AUX(PASTE_TOKENS(name, _Relocation)); @@ -569,6 +569,31 @@ void section_word_Relocation::unpack_data() { _target = address_from_scaled_offset(offset, base); } +void patchable_barrier_Relocation::pack_data_to(CodeSection* dest) { + short* p = (short*) dest->locs_end(); + *p++ = relocInfo::data0_from_int(_target_offset); + *p++ = relocInfo::data1_from_int(_target_offset); + *p++ = checked_cast(_metadata); + dest->set_locs_end((relocInfo*)p); +} + +void patchable_barrier_Relocation::unpack_data() { + assert(datalen() == 3, "Should be int+short fields"); + short* d = data(); + _target_offset = relocInfo::jint_from_data(&d[0]); + _metadata = checked_cast(d[2]); +} + +void patchable_barrier_Relocation::set_target_offset(int32_t target_offset) { + assert(!is_target_offset_resolved(), "Should be"); + assert(datalen() == 3, "Should be int+short fields"); + short* d = data(); + d[0] = relocInfo::data0_from_int(target_offset); + d[1] = relocInfo::data1_from_int(target_offset); + _target_offset = target_offset; + assert(is_target_offset_resolved(), "Should be"); +} + //// miscellaneous methods oop* oop_Relocation::oop_addr() { int n = _oop_index; diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index 9099b534804e..ee5faea4206c 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -268,6 +268,7 @@ class relocInfo { data_prefix_tag = 15, // tag for a prefix (carries data arguments) post_call_nop_type = 16, // A tag for post call nop relocations barrier_type = 17, // GC barrier data + patchable_barrier_type = 18, // Patchable GC barrier type_mask = 31 // A mask which selects only the above values }; @@ -309,6 +310,7 @@ class relocInfo { visitor(trampoline_stub) \ visitor(post_call_nop) \ visitor(barrier) \ + visitor(patchable_barrier) \ public: @@ -1056,6 +1058,43 @@ class barrier_Relocation : public Relocation { barrier_Relocation() : Relocation(relocInfo::barrier_type) { } }; +class patchable_barrier_Relocation : public Relocation { + private: + static const int32_t unresolved = INT32_MAX; + + int32_t _target_offset; + uint16_t _metadata; + + public: + static RelocationHolder spec(uint16_t metadata) { + return RelocationHolder::construct(metadata); + } + + patchable_barrier_Relocation(uint16_t metadata) : Relocation(relocInfo::patchable_barrier_type), + _target_offset(unresolved), _metadata(metadata) { } + + void pack_data_to(CodeSection* dest) override; + void unpack_data() override; + + void copy_into(RelocationHolder& holder) const override; + + uint16_t metadata() const { + return _metadata; + } + bool is_target_offset_resolved() const { + return _target_offset != unresolved; + } + int32_t target_offset() const { + assert(is_target_offset_resolved(), "Should be"); + return _target_offset; + } + void set_target_offset(int32_t target_offset); + + private: + friend class RelocIterator; + friend class RelocationHolder; + patchable_barrier_Relocation() : Relocation(relocInfo::patchable_barrier_type) { } +}; class virtual_call_Relocation : public CallRelocation { diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index c2b57dc80b19..a1b6873b630c 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.cpp @@ -36,7 +36,6 @@ #include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" @@ -45,7 +44,6 @@ address VtableStub::_chunk = nullptr; address VtableStub::_chunk_end = nullptr; -VMReg VtableStub::_receiver_location = VMRegImpl::Bad(); void* VtableStub::operator new(size_t size, int code_size) throw() { @@ -81,8 +79,8 @@ void* VtableStub::operator new(size_t size, int code_size) throw() { void VtableStub::print_on(outputStream* st) const { - st->print("vtable stub (index = %d, receiver_location = %zd, code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "])", - index(), p2i(receiver_location()), p2i(code_begin()), p2i(code_end())); + st->print("vtable stub (index = %d, code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "])", + index(), p2i(code_begin()), p2i(code_end())); } void VtableStub::print() const { print_on(tty); } @@ -124,9 +122,6 @@ int VtableStubs::_itab_stub_size = 0; void VtableStubs::initialize() { - assert(VtableStub::_receiver_location == VMRegImpl::Bad(), "initialized multiple times?"); - - VtableStub::_receiver_location = SharedRuntime::name_for_receiver(); { MutexLocker ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag); for (int i = 0; i < N; i++) { @@ -228,9 +223,9 @@ address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index, bool calle enter(is_vtable_stub, vtable_index, caller_is_c1, s); if (PrintAdapterHandlers) { - tty->print_cr("Decoding VtableStub (%s) %s[%d]@" PTR_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT "] (%zu bytes)", + tty->print_cr("Decoding VtableStub (%s) %s[%d] [" PTR_FORMAT ", " PTR_FORMAT "] (%zu bytes)", caller_is_c1 ? "c1" : "full opt", - is_vtable_stub? "vtbl": "itbl", vtable_index, p2i(VtableStub::receiver_location()), + is_vtable_stub? "vtbl": "itbl", vtable_index, p2i(s->code_begin()), p2i(s->code_end()), pointer_delta(s->code_end(), s->code_begin(), 1)); Disassembler::decode(s->code_begin(), s->code_end()); } @@ -250,13 +245,8 @@ address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index, bool calle inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index, bool caller_is_c1) { - // Assumption: receiver_location < 4 in most cases. - int hash = ((vtable_index << 2) ^ VtableStub::receiver_location()->value()) + vtable_index; - if (caller_is_c1) { - // We have different vtable stubs for C1 and C2. We therefore make sure to get different hashes. - hash = 7 - hash; - } - return (is_vtable_stub ? ~hash : hash) & mask; + int hash = (vtable_index << 2) + (is_vtable_stub ? 2 : 0) + (caller_is_c1 ? 1 : 0); + return hash & mask; } @@ -270,7 +260,7 @@ inline uint VtableStubs::unsafe_hash(address entry_point) { bool is_vtable_stub = *vtable_type_addr == static_cast(VtableStub::Type::vtable_stub); bool caller_is_c1 = (*vtable_caller_type_addr == static_cast(VtableStub::CallerType::c1)); short vtable_index; - static_assert(sizeof(VtableStub::_index) == sizeof(vtable_index), "precondition"); + static_assert(sizeof(VtableStub::_index) == sizeof(vtable_index)); memcpy(&vtable_index, vtable_index_addr, sizeof(vtable_index)); return hash(is_vtable_stub, vtable_index, caller_is_c1); } diff --git a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp index 2ef22fb1fa41..f856b989b669 100644 --- a/src/hotspot/share/code/vtableStubs.hpp +++ b/src/hotspot/share/code/vtableStubs.hpp @@ -31,8 +31,10 @@ #include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" -// A VtableStub holds an individual code stub for a pair (vtable index, #args) for either itables or vtables -// There's a one-to-one relationship between a VtableStub and such a pair. +// VtableStubs creates the code stubs for compiled calls through vtables and itables. There is one +// stub per (table kind, table index, call protocol) triple, where the table kind is either itable +// or vtable and the call protocol indicates either a C1 call or any other call. The stubs are never +// deallocated. They don't need to be GCed because they contain no oops. // A word on VtableStub sizing: // Such a vtable/itable stub consists of the instance data @@ -69,9 +71,6 @@ // As a result, we allocate all but the first code buffers with the same, tightly matching size. // -// VtableStubs creates the code stubs for compiled calls through vtables. -// There is one stub per (vtable index, args_size) pair, and the stubs are -// never deallocated. They don't need to be GCed because they contain no oops. class VtableStub; class VtableStubs : AllStatic { @@ -118,6 +117,9 @@ class VtableStubs : AllStatic { }; +// A VtableStub holds an individual code stub for a particular table kind (itable or vtable), table +// index, and call protocol (C1 or unspecified). There's a one-to-one relationship between a +// VtableStub and such a triple. class VtableStub { private: friend class VtableStubs; @@ -135,7 +137,6 @@ class VtableStub { static address _chunk; // For allocation static address _chunk_end; // For allocation - static VMReg _receiver_location; // Where to find receiver VtableStub* _next; // Pointer to next entry in hash table const short _index; // vtable index @@ -153,7 +154,6 @@ class VtableStub { _type(is_vtable_stub ? Type::vtable_stub : Type::itable_stub), _caller_type(caller_is_c1 ? CallerType::c1 : CallerType::unspecified) {} VtableStub* next() const { return _next; } - static VMReg receiver_location() { return _receiver_location; } void set_next(VtableStub* n) { _next = n; } public: diff --git a/src/hotspot/share/compiler/compilationMemStatInternals.hpp b/src/hotspot/share/compiler/compilationMemStatInternals.hpp index a60508237711..06e008278e90 100644 --- a/src/hotspot/share/compiler/compilationMemStatInternals.hpp +++ b/src/hotspot/share/compiler/compilationMemStatInternals.hpp @@ -91,7 +91,7 @@ class PhaseInfoStack { // A very simple fixed-width FIFO buffer, used for the phase timeline template class SimpleFifo { - STATIC_ASSERT((size * 2) < INT_MAX); + static_assert((size * 2) < INT_MAX); T _v[size]; int _pos; int _oldest; diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 82bd4c160d29..f411a5bf16d8 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -72,7 +72,7 @@ union chunkstamp_t { uint16_t phase_id; }; }; -STATIC_ASSERT(sizeof(chunkstamp_t) == sizeof(chunkstamp_t::raw)); +static_assert(sizeof(chunkstamp_t) == sizeof(chunkstamp_t::raw)); ArenaCounterTable::ArenaCounterTable() { memset(_v, 0, sizeof(_v)); diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index 4e1cdedf3d82..afbff5b064e9 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -73,7 +73,7 @@ class CompileTask : public CHeapObj { "whitebox", "must_be_compiled" }; - STATIC_ASSERT(ARRAY_SIZE(reason_names) == Reason_Count); + static_assert(ARRAY_SIZE(reason_names) == Reason_Count); return reason_names[compile_reason]; } diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 1ad667e51f1e..2f53c346421d 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -272,32 +272,36 @@ void CompilerConfig::set_compilation_policy_flags() { } #ifdef COMPILER2 - if (HotCodeHeap && !is_c2_enabled()) { - warning("HotCodeHeap disabled because C2 is disabled."); - FLAG_SET_ERGO(HotCodeHeap, false); - FLAG_SET_ERGO(HotCodeHeapSize, 0); - } else if (HotCodeHeap) { - if (FLAG_IS_DEFAULT(SegmentedCodeCache)) { - FLAG_SET_ERGO(SegmentedCodeCache, true); - } else if (!SegmentedCodeCache) { - vm_exit_during_initialization("HotCodeHeap requires SegmentedCodeCache enabled"); + // If HotCodeHeap is enabled with an invalid configuration, throw an error and exit. + // Otherwise, enable required flags if possible or disable HotCodeHeap if not. + if (HotCodeHeap) { + if (HotCodeMinSamplingMs > HotCodeMaxSamplingMs) { + vm_exit_during_initialization("HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs."); } - if (FLAG_IS_DEFAULT(NMethodRelocation)) { - FLAG_SET_ERGO(NMethodRelocation, true); - } else if (!NMethodRelocation) { - vm_exit_during_initialization("HotCodeHeap requires NMethodRelocation enabled"); + const char* warn = nullptr; + if (!is_c2_enabled()) { + warn = "HotCodeHeap disabled and HotCodeHeapSize zeroed because C2 is disabled."; + } else if (!SegmentedCodeCache && !FLAG_IS_DEFAULT(SegmentedCodeCache)) { + warn = "HotCodeHeap disabled and HotCodeHeapSize zeroed because SegmentedCodeCache is disabled."; + } else if (!NMethodRelocation && !FLAG_IS_DEFAULT(NMethodRelocation)) { + warn = "HotCodeHeap disabled and HotCodeHeapSize zeroed because NMethodRelocation is disabled."; } - if (HotCodeMinSamplingMs > HotCodeMaxSamplingMs) { - vm_exit_during_initialization("HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs"); + if (warn != nullptr) { + warning("%s", warn); + FLAG_SET_ERGO(HotCodeHeap, false); + FLAG_SET_ERGO(HotCodeHeapSize, 0); + } else { + FLAG_SET_ERGO_IF_DEFAULT(SegmentedCodeCache, true); + FLAG_SET_ERGO_IF_DEFAULT(NMethodRelocation, true); } } else if (HotCodeHeapSize > 0) { - vm_exit_during_initialization("HotCodeHeapSize requires HotCodeHeap enabled"); + vm_exit_during_initialization("HotCodeHeapSize requires HotCodeHeap enabled."); } #else if (HotCodeHeapSize > 0) { - vm_exit_during_initialization("HotCodeHeapSize requires C2 present"); + vm_exit_during_initialization("HotCodeHeapSize requires C2 present."); } #endif // COMPILER2 diff --git a/src/hotspot/share/compiler/compilerEvent.cpp b/src/hotspot/share/compiler/compilerEvent.cpp index 323da8056fc9..4dd1be73bece 100644 --- a/src/hotspot/share/compiler/compilerEvent.cpp +++ b/src/hotspot/share/compiler/compilerEvent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,7 +89,7 @@ int CompilerEvent::PhaseEvent::get_phase_id(const char* phase_name, bool may_exi { PhaseTypeGuard guard(sync); if (_phase_names == nullptr) { - _phase_names = new (mtInternal) GrowableArray(100, mtCompiler); + _phase_names = new (mtCompiler) GrowableArray(100, mtCompiler); register_jfr_serializer = true; } else if (may_exist) { index = lookup_phase(phase_name); diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index 7d2b4de11825..972da99ce1da 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -1043,7 +1043,7 @@ class LineCopy : StackObj { const char* _copy; public: LineCopy(char* line) { - _copy = os::strdup(line, mtInternal); + _copy = os::strdup(line, mtCompiler); } ~LineCopy() { os::free((void*)_copy); diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 4fb566ac1fd2..8f84bf59387e 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -189,7 +189,7 @@ const char* G1CardSetConfiguration::mem_object_type_name_str(uint index) { } void G1CardSetCoarsenStats::reset() { - STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); + static_assert(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { _coarsen_from[i].store_relaxed(0); _coarsen_collision[i].store_relaxed(0); @@ -197,7 +197,7 @@ void G1CardSetCoarsenStats::reset() { } void G1CardSetCoarsenStats::set(G1CardSetCoarsenStats& other) { - STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); + static_assert(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { _coarsen_from[i].store_relaxed(other._coarsen_from[i].load_relaxed()); _coarsen_collision[i].store_relaxed(other._coarsen_collision[i].load_relaxed()); @@ -205,7 +205,7 @@ void G1CardSetCoarsenStats::set(G1CardSetCoarsenStats& other) { } void G1CardSetCoarsenStats::subtract_from(G1CardSetCoarsenStats& other) { - STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); + static_assert(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { _coarsen_from[i].store_relaxed(other._coarsen_from[i].load_relaxed() - _coarsen_from[i].load_relaxed()); _coarsen_collision[i].store_relaxed(other._coarsen_collision[i].load_relaxed() - _coarsen_collision[i].load_relaxed()); diff --git a/src/hotspot/share/gc/g1/g1CardTable.hpp b/src/hotspot/share/gc/g1/g1CardTable.hpp index ddbfa841c24d..2c3f52fa0602 100644 --- a/src/hotspot/share/gc/g1/g1CardTable.hpp +++ b/src/hotspot/share/gc/g1/g1CardTable.hpp @@ -98,7 +98,7 @@ class G1CardTable : public CardTable { static const size_t WordAllDirty = 0; static const size_t WordAllFromRemset = (SIZE_MAX / 255) * g1_from_remset_card; - STATIC_ASSERT(BitsPerByte == 8); + static_assert(BitsPerByte == 8); static const size_t WordAlreadyScanned = (SIZE_MAX / 255) * g1_card_already_scanned; G1CardTable(MemRegion whole_heap): CardTable(whole_heap), _listener() { diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp index 771ab1a7fa2a..37cd4b586695 100644 --- a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp @@ -107,7 +107,7 @@ class G1CodeRootSetHashTable : public CHeapObj { // The CHT only uses the bits smaller than HashTable::DEFAULT_MAX_SIZE_LOG2, so // try to increase the randomness by incorporating the upper bits of the // address too. - STATIC_ASSERT(HashTable::DEFAULT_MAX_SIZE_LOG2 <= sizeof(uint32_t) * BitsPerByte); + static_assert(HashTable::DEFAULT_MAX_SIZE_LOG2 <= sizeof(uint32_t) * BitsPerByte); #ifdef _LP64 return hash((uint32_t)value ^ (uint32_t(value >> 32))); #else diff --git a/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.hpp b/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.hpp index 2746315a8a8c..aa8d1df64129 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArenaFreePool.hpp @@ -42,7 +42,7 @@ class G1MonotonicArenaMemoryStats { G1MonotonicArenaMemoryStats(); void add(G1MonotonicArenaMemoryStats const other) { - STATIC_ASSERT(ARRAY_SIZE(_num_segments) == ARRAY_SIZE(_num_mem_sizes)); + static_assert(ARRAY_SIZE(_num_segments) == ARRAY_SIZE(_num_mem_sizes)); for (uint i = 0; i < ARRAY_SIZE(_num_mem_sizes); i++) { _num_mem_sizes[i] += other._num_mem_sizes[i]; _num_segments[i] += other._num_segments[i]; diff --git a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp index 7889fd4fb31e..b8a99d5be072 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp @@ -72,7 +72,7 @@ class G1PreEvacuateCollectionSetBatchTask::JavaThreadRetireTLABs : public G1Abst } ~JavaThreadRetireTLABs() { - static_assert(std::is_trivially_destructible::value, "must be"); + static_assert(std::is_trivially_destructible::value); FREE_C_HEAP_ARRAY(_local_tlab_stats); } diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 0164a18971a6..6b17e95c1e1e 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -267,7 +267,7 @@ product(size_t, G1HeapRegionSize, 0, \ "Size of the G1 regions.") \ range(0, NOT_LP64(32*M) LP64_ONLY(512*M)) \ - constraint(G1HeapRegionSizeConstraintFunc,AfterMemoryInit) \ + constraint(G1HeapRegionSizeConstraintFunc, AfterErgo) \ \ product(uint, G1ConcRefinementThreads, 0, \ "The number of parallel remembered set update threads. " \ diff --git a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp index 2ad3500de4a8..9b5afc389ef9 100644 --- a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp +++ b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp @@ -81,11 +81,11 @@ JVMFlag::Error G1HeapRegionSizeConstraintFunc(size_t value, bool verbose) { if (!UseG1GC) return JVMFlag::SUCCESS; // Default value of G1HeapRegionSize=0 means will be set ergonomically. - if (FLAG_IS_CMDLINE(G1HeapRegionSize) && (value < G1HeapRegionBounds::min_size())) { + if (value != 0 && value < G1HeapRegionBounds::min_size()) { JVMFlag::printError(verbose, "G1HeapRegionSize (%zu) must be " - "greater than or equal to ergonomic heap region minimum size\n", - value); + "greater than or equal to ergonomic heap region minimum size (%zuM)\n", + value, G1HeapRegionBounds::min_size() / M); return JVMFlag::VIOLATES_CONSTRAINT; } else { return JVMFlag::SUCCESS; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index 2dab21623ad3..0cb3887833ae 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -169,7 +169,7 @@ class ParallelScavengeHeap : public CollectedHeap { // The alignment used for spaces in young gen and old gen constexpr static size_t default_space_alignment() { constexpr size_t alignment = 64 * K * HeapWordSize; - static_assert(is_power_of_2(alignment), "inv"); + static_assert(is_power_of_2(alignment)); return alignment; } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index 085541770482..5a66bebe2b2a 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -125,8 +125,8 @@ class ParCompactionManager : public CHeapObj { // reduce threads contention. class MarkingStatsCache : public CHeapObj { constexpr static size_t num_entries = 1024; - static_assert(is_power_of_2(num_entries), "inv"); - static_assert(num_entries > 0, "inv"); + static_assert(is_power_of_2(num_entries)); + static_assert(num_entries > 0); constexpr static size_t entry_mask = num_entries - 1; diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 1aef37b1ecc9..ca91d59ea1f2 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -845,7 +845,7 @@ bool PSParallelCompact::invoke(bool clear_all_soft_refs, { if (heap->young_gen()->reserved_size() > 0 && heap->young_gen()->is_from_to_layout()) { - static_assert(to_space_id < from_space_id, "inv"); + static_assert(to_space_id < from_space_id); // Ensure to-from layout so that all objs are slid to lower address, // according to the order in SpaceId. heap->young_gen()->swap_spaces(); diff --git a/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp b/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp index 0860b28ba393..3a91f8b58c2a 100644 --- a/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp +++ b/src/hotspot/share/gc/shared/adaptiveSizePolicy.hpp @@ -45,7 +45,7 @@ class AdaptiveSizePolicy : public CHeapObj { // means objs haven't got time to die yet, so #promoted objs will be high. // Default: 100ms. static constexpr double MinGCDistanceSecond = 0.100; - static_assert(MinGCDistanceSecond >= 0.001, "inv"); + static_assert(MinGCDistanceSecond >= 0.001); // pause and interval times for collections static elapsedTimer _minor_timer; diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp index cd01ddda09c5..d56a2d066de4 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.hpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.hpp @@ -75,6 +75,8 @@ class BarrierSetNMethod: public CHeapObj { virtual oop oop_load_no_keepalive(const nmethod* nm, int index); virtual oop oop_load_phantom(const nmethod* nm, int index); + + virtual void finalize_relocations(nmethod* nm) {} }; diff --git a/src/hotspot/share/gc/shared/bufferNode.hpp b/src/hotspot/share/gc/shared/bufferNode.hpp index e4e2ff23fb19..f317347ad4a9 100644 --- a/src/hotspot/share/gc/shared/bufferNode.hpp +++ b/src/hotspot/share/gc/shared/bufferNode.hpp @@ -35,7 +35,7 @@ class BufferNode { using InternalSizeType = LP64_ONLY(uint32_t) NOT_LP64(uint16_t); - static_assert(sizeof(InternalSizeType) <= sizeof(size_t), "assumption"); + static_assert(sizeof(InternalSizeType) <= sizeof(size_t)); InternalSizeType _index; InternalSizeType _capacity; diff --git a/src/hotspot/share/gc/shared/cardTable.hpp b/src/hotspot/share/gc/shared/cardTable.hpp index 93099683056a..c518a9fdd8d4 100644 --- a/src/hotspot/share/gc/shared/cardTable.hpp +++ b/src/hotspot/share/gc/shared/cardTable.hpp @@ -38,7 +38,7 @@ class CardTable: public CHeapObj { // All code generators assume that the size of a card table entry is one byte. // They need to be updated to reflect any change to this. // This code can typically be found by searching for the byte_map_base() method. - STATIC_ASSERT(sizeof(CardValue) == 1); + static_assert(sizeof(CardValue) == 1); protected: // The declaration order of these const fields is important; see the diff --git a/src/hotspot/share/gc/shared/gcTraceTime.inline.hpp b/src/hotspot/share/gc/shared/gcTraceTime.inline.hpp index 388087c55575..51d6aadec094 100644 --- a/src/hotspot/share/gc/shared/gcTraceTime.inline.hpp +++ b/src/hotspot/share/gc/shared/gcTraceTime.inline.hpp @@ -174,8 +174,8 @@ class GCTraceTimeLoggerWrapper : public GCTraceTimeLoggerImpl { log_heap_usage, LogTargetHandle::create(), LogTargetHandle::create()) { - STATIC_ASSERT(T0 != LogTag::__NO_TAG); // Need some tag to log on. - STATIC_ASSERT(T4 == LogTag::__NO_TAG); // Need to leave at least the last tag for the "start" tag in log_start() + static_assert(T0 != LogTag::__NO_TAG); // Need some tag to log on. + static_assert(T4 == LogTag::__NO_TAG); // Need to leave at least the last tag for the "start" tag in log_start() } }; @@ -195,8 +195,8 @@ class GCTraceTimeWrapper : public StackObj { timer, gc_cause, log_heap_usage) { - STATIC_ASSERT(T0 != LogTag::__NO_TAG); // Need some tag to log on. - STATIC_ASSERT(T4 == LogTag::__NO_TAG); // Need to leave at least the last tag for the "start" tag in log_start() + static_assert(T0 != LogTag::__NO_TAG); // Need some tag to log on. + static_assert(T4 == LogTag::__NO_TAG); // Need to leave at least the last tag for the "start" tag in log_start() } }; diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index 8aea565cdf74..cb7fbc60e849 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -223,8 +223,8 @@ OopStorage::Block::Block(const OopStorage* owner, void* memory) : _deferred_updates_next(nullptr), _release_refcount(0) { - STATIC_ASSERT(_data_pos == 0); - STATIC_ASSERT(section_size * section_count == ARRAY_SIZE(_data)); + static_assert(_data_pos == 0); + static_assert(section_size * section_count == ARRAY_SIZE(_data)); assert(offset_of(Block, _data) == _data_pos, "invariant"); assert(owner != nullptr, "null owner"); assert(is_aligned(this, block_alignment), "misaligned block"); @@ -244,7 +244,7 @@ OopStorage::Block::~Block() { size_t OopStorage::Block::allocation_size() { // _data must be first member, so aligning Block aligns _data. - STATIC_ASSERT(_data_pos == 0); + static_assert(_data_pos == 0); return sizeof(Block) + block_alignment - sizeof(void*); } @@ -301,12 +301,12 @@ void OopStorage::Block::set_active_index(size_t index) { } size_t OopStorage::Block::active_index_safe(const Block* block) { - STATIC_ASSERT(sizeof(intptr_t) == sizeof(block->_active_index)); + static_assert(sizeof(intptr_t) == sizeof(block->_active_index)); // Be careful, because block could be a false positive from block_for_ptr. assert(block != nullptr, "precondition"); uintptr_t block_addr = reinterpret_cast(block); uintptr_t index_loc = block_addr + offset_of(Block, _active_index); - static_assert(sizeof(size_t) == sizeof(intptr_t), "assumption"); + static_assert(sizeof(size_t) == sizeof(intptr_t)); return static_cast(SafeFetchN(reinterpret_cast(index_loc), 0)); } @@ -347,7 +347,7 @@ uintx OopStorage::Block::allocate_all() { OopStorage::Block* OopStorage::Block::new_block(const OopStorage* owner) { // _data must be first member: aligning block => aligning _data. - STATIC_ASSERT(_data_pos == 0); + static_assert(_data_pos == 0); size_t size_needed = allocation_size(); void* memory = NEW_C_HEAP_ARRAY_RETURN_NULL(char, size_needed, owner->mem_tag()); if (memory == nullptr) { @@ -371,7 +371,7 @@ void OopStorage::Block::delete_block(const Block& block) { // require additional validation of the result. OopStorage::Block* OopStorage::Block::block_for_ptr(const OopStorage* owner, const oop* ptr) { - STATIC_ASSERT(_data_pos == 0); + static_assert(_data_pos == 0); assert(ptr != nullptr, "precondition"); // Blocks are allocated section-aligned, so get the containing section. uintptr_t section_start = align_down(reinterpret_cast(ptr), block_alignment); @@ -386,7 +386,7 @@ OopStorage::Block::block_for_ptr(const OopStorage* owner, const oop* ptr) { intptr_t owner_addr = reinterpret_cast(owner); for (unsigned i = 0; i < section_count; ++i, section += section_size_in_bytes) { uintptr_t owner_loc = section + offset_of(Block, _owner_address); - static_assert(sizeof(OopStorage*) == sizeof(intptr_t), "assumption"); + static_assert(sizeof(OopStorage*) == sizeof(intptr_t)); if (SafeFetchN(reinterpret_cast(owner_loc), 0) == owner_addr) { return reinterpret_cast(section); } diff --git a/src/hotspot/share/gc/shared/oopStorageSet.hpp b/src/hotspot/share/gc/shared/oopStorageSet.hpp index 867172c41ad7..b7efbfa9e0b1 100644 --- a/src/hotspot/share/gc/shared/oopStorageSet.hpp +++ b/src/hotspot/share/gc/shared/oopStorageSet.hpp @@ -50,7 +50,7 @@ class OopStorageSet : public AllStatic { static const uint weak_start = strong_end; static const uint weak_end = weak_start + weak_count; - static_assert(all_end == weak_end, "invariant"); + static_assert(all_end == weak_end); static OopStorage* _storages[all_count]; diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index 4679ac2f51c3..3ef2927fe9a7 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -100,7 +100,7 @@ void PartialArrayStateAllocator::release(PartialArrayState* state) { } else { OrderAccess::acquire(); // Don't need to call destructor; can't if not destructible. - static_assert(!std::is_destructible::value, "expected"); + static_assert(!std::is_destructible::value); _free_list = ::new (state) FreeListEntry(_free_list); } } diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index fc7ef75c1bed..a75a045638a3 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -74,7 +74,7 @@ static const char* ReferenceTypeNames[REF_PHANTOM + 1] = { "None", "SoftReference", "WeakReference", "FinalReference", "PhantomReference" }; -STATIC_ASSERT((REF_PHANTOM + 1) == ARRAY_SIZE(ReferenceTypeNames)); +static_assert((REF_PHANTOM + 1) == ARRAY_SIZE(ReferenceTypeNames)); static const char* phase_enum_2_phase_string(ReferenceProcessor::RefProcPhases phase) { ASSERT_PHASE(phase); diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp index ab1b15b04478..f57d9d53fa3f 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp @@ -59,7 +59,7 @@ StringDedup::Stat StringDedup::_total_stat{}; // Configuration for predicates used to decide whether to deduplicate. // The initial values are suitable for deduplication being disabled. const Klass* StringDedup::_string_klass_or_null = nullptr; // No klass will match. -static_assert(markWord::max_age < UINT_MAX, "assumption"); +static_assert(markWord::max_age < UINT_MAX); uint StringDedup::_enabled_age_threshold = UINT_MAX; // Age never equals max. uint StringDedup::_enabled_age_limit = 0; // Age is never less than zero. diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 546efa4774bd..e2e78c7bc4a0 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -523,7 +523,7 @@ bool StringDedup::Table::try_deduplicate_shared(oop java_string) { assert(value != nullptr, "precondition"); assert(TypeArrayKlass::cast(value->klass())->element_type() == T_BYTE, "precondition"); int length = value->length(); - static_assert(sizeof(jchar) == 2 * sizeof(jbyte), "invariant"); + static_assert(sizeof(jchar) == 2 * sizeof(jbyte)); assert(((length & 1) == 0) || CompactStrings, "invariant"); if ((length & 1) == 0) { // If the length of the byte array is even, then the value array could be diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index c942b6cc3e99..bc509b7648a9 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -292,7 +292,7 @@ inline int randomParkAndMiller(int *seed0) { const int m = 2147483647; const int q = 127773; /* m div a */ const int r = 2836; /* m mod a */ - STATIC_ASSERT(sizeof(int) == 4); + static_assert(sizeof(int) == 4); int seed = *seed0; int hi = seed / q; int lo = seed % q; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 19ddeb57e612..2b8c12c41a68 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -912,7 +912,7 @@ void ShenandoahBarrierStubC2::load_post(MacroAssembler* masm, const MachNode* no check |= needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0; check |= needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0; check |= needs_load_ref_barrier_weak(node) ? ShenandoahHeap::WEAK_ROOTS : 0; - stub->enter_if_gc_state(*masm, check, tmp1); + stub->enter_if_gc_state(*masm, check, tmp1, tmp2); } } @@ -921,7 +921,7 @@ void ShenandoahBarrierStubC2::store_pre(MacroAssembler* masm, const MachNode* no if (needs_slow_barrier(node)) { assert(!needs_load_ref_barrier(node), "Should not be required for stores"); ShenandoahBarrierStubC2* const stub = create(node, tmp1, addr, tmp2, tmp3, narrow, /* do_load = */ true); - stub->enter_if_gc_state(*masm, ShenandoahHeap::MARKING, tmp1); + stub->enter_if_gc_state(*masm, ShenandoahHeap::MARKING, tmp1, tmp2); } } @@ -939,7 +939,7 @@ void ShenandoahBarrierStubC2::load_store_pre(MacroAssembler* masm, const MachNod check |= needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0; check |= needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0; assert(!needs_load_ref_barrier_weak(node), "Not supported for Load/Stores"); - stub->enter_if_gc_state(*masm, check, tmp1); + stub->enter_if_gc_state(*masm, check, tmp1, tmp2); } } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 065c746d93ce..c0a8938526c5 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -168,7 +168,16 @@ class ShenandoahBarrierStubC2 : public BarrierStubC2 { void maybe_far_jump_if_zero(MacroAssembler& masm, Register reg); - void enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp); + void patchable_jump(MacroAssembler& masm, char gc_state, bool jump_when_state, Register tmp1, Register tmp2, Label* L_target); + + void patchable_jump_if_gc_state(MacroAssembler& masm, const char gc_state, Register tmp1, Register tmp2, Label* L_target) { + patchable_jump(masm, gc_state, true, tmp1, tmp2, L_target); + } + void patchable_jump_if_not_gc_state(MacroAssembler& masm, const char gc_state, Register tmp1, Register tmp2, Label* L_target) { + patchable_jump(masm, gc_state, false, tmp1, tmp2, L_target); + } + + void enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp1, Register tmp2); void keepalive(MacroAssembler& masm, Label* L_done); void lrb(MacroAssembler& masm); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 107f126b44d5..7811940cc6f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -179,6 +179,9 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { } else if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { ShenandoahContextEvacuateUpdateRootsClosure oops; StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); + } else { + ShenandoahNoOpClosure oops; + StackWatermarkSet::finish_processing(JavaThread::cast(thread), &oops, StackWatermarkKind::gc); } _heap->flush_region_pin_cache(JavaThread::cast(thread)); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index 1929313148dc..5adf5b034506 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -23,21 +23,28 @@ */ +#include "code/relocInfo.hpp" +#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahBarrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahNMethod.inline.hpp" +#include "gc/shenandoah/shenandoahStackChunkGCData.hpp" +#include "gc/shenandoah/shenandoahStackWatermark.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" +#include "runtime/icache.hpp" +#include "runtime/orderAccess.hpp" #include "runtime/threadWXSetters.inline.hpp" bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { if (!is_armed(nm)) { - // Some other thread got here first and healed the oops - // and disarmed the nmethod. No need to continue. + // Some other thread got here first and healed the oops. + // No need to continue. We only need to sync up the changes done by others. + cross_modify_fence(); return true; } @@ -46,8 +53,9 @@ bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { ShenandoahNMethodLocker locker(lock); if (!is_armed(nm)) { - // Some other thread managed to complete while we were - // waiting for lock. No need to continue. + // Some other thread managed to complete while we were waiting for lock. + // No need to continue. We only need to sync up the changes done by others. + cross_modify_fence(); return true; } @@ -65,13 +73,59 @@ bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return false; } - // Heal oops - ShenandoahNMethod::heal_nmethod(nm); + // Handle oops and jumps. + { + ICacheInvalidationContext icic; + ShenandoahNMethod::handle_oops(nm, &icic); + ShenandoahNMethod::handle_jumps(nm, &icic); + } // CodeCache unloading support nm->mark_as_maybe_on_stack(); // Disarm ShenandoahNMethod::disarm_nmethod(nm); + + // Paranoia: sync up the changes done by others, even though we did a lot ourselves. + cross_modify_fence(); + return true; } + +void ShenandoahBarrierSetNMethod::cross_modify_fence() { + // For Java threads that can execute the nmethod code, we need to sync up + // the code changes with current execution. Non-Java threads do not execute + // the code, and thus do not need this fence. (VM has VerifyCrossModifyFence code + // that barfs when we attempt to do this.) + if (Thread::current()->is_Java_thread()) { + OrderAccess::cross_modify_fence(); + } +} + +void ShenandoahBarrierSetNMethod::finalize_relocations(nmethod* nm) { + RelocIterator iter(nm); + while (iter.next()) { + if (iter.type() == relocInfo::patchable_barrier_type) { + patchable_barrier_Relocation* r = iter.patchable_barrier_reloc(); + if (!r->is_target_offset_resolved()) { + address pc = r->addr(); + address target = ShenandoahBarrierSetAssembler::parse_jump_address(pc); + r->set_target_offset(target - nm->code_begin()); + } else { + // Already set. This is likely nmethod relocation, so just trust + // the existing relocation data. + } + } + } +} + +void ShenandoahBarrierSetNMethod::arm_all_nmethods() { + BarrierSetNMethod::arm_all_nmethods(); + + // Arming should also activate stack watermark machinery. + // See ShenandoahNMethod::patch_jump. + ShenandoahStackWatermark::change_epoch_id(); + + // All currently allocated stack chunks now need to be fixed up. + ShenandoahStackChunkGCData::change_epoch_id(); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.hpp index 4808baf5e3f6..36ee39330bba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.hpp @@ -35,11 +35,17 @@ class ShenandoahBarrierSetNMethod : public BarrierSetNMethod { private: ShenandoahHeap* _heap; + void cross_modify_fence(); + public: ShenandoahBarrierSetNMethod(ShenandoahHeap* heap) : _heap(heap) { } - virtual bool nmethod_entry_barrier(nmethod* nm); + bool nmethod_entry_barrier(nmethod* nm) override; + + void finalize_relocations(nmethod* nm) override; + + void arm_all_nmethods() override; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHBARRIERSETNMETHOD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 8b082b4127b0..58e47841819f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -200,13 +200,6 @@ class ShenandoahCleanUpdateWeakOopsClosure : public OopClosure { inline void do_oop(narrowOop* p); }; -class ShenandoahNMethodAndDisarmClosure : public NMethodToOopClosure { -public: - inline ShenandoahNMethodAndDisarmClosure(OopClosure* cl); - inline void do_nmethod(nmethod* nm); -}; - - // // ========= Update References // @@ -262,6 +255,12 @@ class ShenandoahFlushSATB : public ThreadClosure { // ========= Utilities // +class ShenandoahNoOpClosure : public OopClosure { +public: + inline void do_oop(oop* p) { } + inline void do_oop(narrowOop* p) { } +}; + #ifdef ASSERT class ShenandoahAssertNotForwardedClosure : public OopClosure { private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 671d9586073a..fcd564f1d433 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -194,17 +194,6 @@ void ShenandoahCleanUpdateWeakOopsClosure::do_oo ShouldNotReachHere(); } -ShenandoahNMethodAndDisarmClosure::ShenandoahNMethodAndDisarmClosure(OopClosure* cl) : - NMethodToOopClosure(cl, true /* fix_relocations */) {} - -void ShenandoahNMethodAndDisarmClosure::do_nmethod(nmethod* nm) { - assert(nm != nullptr, "Sanity"); - assert(!ShenandoahNMethod::gc_data(nm)->is_unregistered(), "Should not be here"); - NMethodToOopClosure::do_nmethod(nm); - ShenandoahNMethod::disarm_nmethod(nm); -} - - // // ========= Update References // diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index d1c25eb49b4f..2c0effaa21e2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,10 +29,12 @@ #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahNMethod.inline.hpp" +#include "gc/shenandoah/shenandoahStackWatermark.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "runtime/atomicAccess.hpp" +#include "runtime/icache.hpp" #include "utilities/powerOfTwo.hpp" @@ -53,40 +55,6 @@ void ShenandoahCodeRoots::unregister_nmethod(nmethod* nm) { _nmethod_table->unregister_nmethod(nm); } -void ShenandoahCodeRoots::arm_nmethods() { - BarrierSet::barrier_set()->barrier_set_nmethod()->arm_all_nmethods(); -} - -class ShenandoahDisarmNMethodClosure : public NMethodClosure { -public: - virtual void do_nmethod(nmethod* nm) { - ShenandoahNMethod::disarm_nmethod(nm); - } -}; - -class ShenandoahDisarmNMethodsTask : public WorkerTask { -private: - ShenandoahDisarmNMethodClosure _cl; - ShenandoahConcurrentNMethodIterator _iterator; - -public: - ShenandoahDisarmNMethodsTask() : - WorkerTask("Shenandoah Disarm NMethods"), - _iterator(ShenandoahCodeRoots::table()) { - assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint"); - } - - virtual void work(uint worker_id) { - ShenandoahParallelWorkerSession worker_session(worker_id); - _iterator.nmethods_do(&_cl); - } -}; - -void ShenandoahCodeRoots::disarm_nmethods() { - ShenandoahDisarmNMethodsTask task; - ShenandoahHeap::heap()->workers()->run_task(&task); -} - class ShenandoahNMethodUnlinkClosure : public NMethodClosure { private: bool _unloading_occurred; @@ -116,7 +84,8 @@ class ShenandoahNMethodUnlinkClosure : public NMethodClosure { // Heal oops if (_bs->is_armed(nm)) { - ShenandoahNMethod::heal_nmethod_metadata(nm_data); + ICacheInvalidationContext icic; + ShenandoahNMethod::heal_nmethod_metadata(nm_data, &icic); // Must remain armed to complete remaining work in nmethod entry barrier assert(_bs->is_armed(nm), "Should remain armed"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index d395b4516f4f..72f061627392 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -67,8 +67,7 @@ class ShenandoahCodeRoots : public AllStatic { // Concurrent nmethod unloading support static void unlink(WorkerThreads* workers, bool unloading_occurred); static void purge(); - static void arm_nmethods(); - static void disarm_nmethods(); + static int disarmed_value() { return _disarmed_value; } static int* disarmed_value_address() { return &_disarmed_value; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 09ca7e4315d4..decd2226f23e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -25,6 +25,7 @@ */ +#include "code/codeCache.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/collectorCounters.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" @@ -52,6 +53,7 @@ #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "memory/allocation.hpp" #include "prims/jvmtiTagMap.hpp" +#include "runtime/icache.hpp" #include "runtime/vmThread.hpp" #include "utilities/events.hpp" @@ -216,7 +218,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { } // Roots processing is complete, put the weak roots flag down. - entry_final_roots(); + vmop_entry_final_roots(); // Continue the cycle with evacuation and optional update-refs. // This may be skipped if there is nothing to evacuate. @@ -365,6 +367,17 @@ void ShenandoahConcurrentGC::vmop_entry_final_update_refs() { VMThread::execute(&op); } +void ShenandoahConcurrentGC::vmop_entry_final_roots() { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->stw_collection_counters()); + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_roots_gross); + + // This phase does not use workers, no need for setup + heap->try_inject_pin(); + VM_ShenandoahFinalRoots op(this); + VMThread::execute(&op); +} + void ShenandoahConcurrentGC::vmop_entry_final_verify() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->stw_collection_counters()); @@ -777,8 +790,7 @@ void ShenandoahConcurrentGC::op_init_mark() { OrderAccess::fence(); // Arm nmethods/stack for concurrent processing - ShenandoahCodeRoots::arm_nmethods(); - ShenandoahStackWatermark::change_epoch_id(); + CodeCache::arm_all_nmethods(); { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::init_propagate_gc_state); @@ -834,11 +846,6 @@ void ShenandoahConcurrentGC::op_final_mark() { heap->set_evacuation_in_progress(true); // From here on, we need to update references. heap->set_has_forwarded_objects(true); - - // Arm nmethods/stack for concurrent processing - ShenandoahCodeRoots::arm_nmethods(); - ShenandoahStackWatermark::change_epoch_id(); - } else { if (ShenandoahVerify) { ShenandoahTimingsTracker v(ShenandoahPhaseTimings::final_mark_verify); @@ -851,6 +858,9 @@ void ShenandoahConcurrentGC::op_final_mark() { } } + // Arm nmethods/stack for concurrent processing + CodeCache::arm_all_nmethods(); + { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_mark_propagate_gc_state); heap->propagate_gc_state_to_all_threads(); @@ -1070,8 +1080,8 @@ class ShenandoahEvacUpdateCodeCacheClosure : public NMethodClosure { void do_nmethod(nmethod* n) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(n); ShenandoahNMethodLocker locker(data->lock()); - data->oops_do(&_cl, /* fix_relocations = */ true); - ShenandoahNMethod::disarm_nmethod(n); + ICacheInvalidationContext icic; + data->oops_do(&_cl, /* fix_relocations = */ true, &icic); } }; @@ -1252,6 +1262,9 @@ void ShenandoahConcurrentGC::op_final_update_refs() { heap->rebuild_free_set(true /*concurrent*/); _generation->heuristics()->start_idle_span(); + // Final pause: update GC barriers to idle state. + CodeCache::arm_all_nmethods(); + { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_update_refs_propagate_gc_state); heap->propagate_gc_state_to_all_threads(); @@ -1260,12 +1273,11 @@ void ShenandoahConcurrentGC::op_final_update_refs() { void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); - TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent final roots", ""); - ShenandoahConcurrentSubphase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); + SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Roots", ""); + ShenandoahPauseSubphase gc_phase(msg, ShenandoahPhaseTimings::final_roots); EventMark em("%s", msg); - heap->concurrent_final_roots(); + heap->op_final_roots(); } void ShenandoahConcurrentGC::op_verify_final() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index cf1398e3de69..7b49b6626e23 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -41,6 +41,7 @@ class VM_ShenandoahFinalUpdateRefs; class ShenandoahConcurrentGC : public ShenandoahGC { friend class VM_ShenandoahInitMark; friend class VM_ShenandoahFinalMarkStartEvac; + friend class VM_ShenandoahFinalRoots; friend class VM_ShenandoahInitUpdateRefs; friend class VM_ShenandoahFinalUpdateRefs; friend class VM_ShenandoahFinalVerify; @@ -69,12 +70,14 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void vmop_entry_final_mark(); void vmop_entry_init_update_refs(); void vmop_entry_final_update_refs(); + void vmop_entry_final_roots(); void vmop_entry_final_verify(); // Entry methods to normally STW GC operations. These set up logging, monitoring // and workers for next VM operation void entry_init_mark(); void entry_final_mark(); + void entry_final_roots(); void entry_init_update_refs(); void entry_final_update_refs(); void entry_final_verify(); @@ -92,7 +95,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_strong_roots(); void entry_cleanup_early(); void entry_complete_abbreviated_cycle(); - void entry_final_roots(); void entry_evacuate(); void entry_update_thread_roots(); void entry_update_card_table(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 824f7bb432af..578c2899d4db 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -24,6 +24,7 @@ */ +#include "code/codeCache.hpp" #include "gc/shared/collectorCounters.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentMark.hpp" @@ -301,10 +302,6 @@ void ShenandoahDegenGC::op_degenerated() { assert(!heap->cancelled_gc(), "STW reference update can not OOM"); } - // Disarm nmethods that armed in concurrent cycle. - // In above case, update roots should disarm them - ShenandoahCodeRoots::disarm_nmethods(); - op_cleanup_complete(); if (heap->mode()->is_generational()) { @@ -318,6 +315,9 @@ void ShenandoahDegenGC::op_degenerated() { DEBUG_ONLY(heap->assert_no_self_forwards()); + // Leaving degenerated GC, we need to flip barriers back to idle. + CodeCache::arm_all_nmethods(); + if (ShenandoahVerify) { heap->verifier()->verify_after_degenerated(_generation); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index cab0db7e78a8..11b98a10c12d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -25,6 +25,7 @@ */ +#include "code/codeCache.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/continuationGCSupport.hpp" #include "gc/shared/fullGCForwarding.inline.hpp" @@ -128,6 +129,9 @@ void ShenandoahFullGC::op_full(GCCause::Cause cause) { _generation->heuristics()->record_success_full(); heap->shenandoah_policy()->record_success_full(); + // Leaving full GC, we need to flip barriers back to idle. + CodeCache::arm_all_nmethods(); + { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::full_gc_propagate_gc_state); heap->propagate_gc_state_to_all_threads(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 6c3ee675cd0c..7580d45ed3f2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -167,9 +167,8 @@ ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread: heuristics->log_trigger("Handle Allocation Failure"); - // Do not bother with degenerated cycle if old generation evacuation failed or if humongous allocation failed - if (ShenandoahDegeneratedGC && heuristics->should_degenerate_cycle() && - !old_gen_evacuation_failed && request.cause != GCCause::_shenandoah_humongous_allocation_failure) { + // Do not bother with degenerated cycle if old generation evacuation failed + if (ShenandoahDegeneratedGC && heuristics->should_degenerate_cycle() && !old_gen_evacuation_failed) { heuristics->record_allocation_failure_gc(); _heap->shenandoah_policy()->record_alloc_failure_to_degenerated(_degen_point); return stw_degenerated; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 63a8666f816f..e58d68627e47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -161,8 +161,8 @@ void ShenandoahGenerationalHeap::start_idle_span() { } bool ShenandoahGenerationalHeap::requires_barriers(stackChunkOop obj) const { - if (is_idle()) { - return false; + if (ShenandoahHeap::requires_barriers(obj)) { + return true; } if (is_concurrent_young_mark_in_progress() && is_in_young(obj) && !marking_context()->allocated_after_mark_start(obj)) { @@ -175,11 +175,6 @@ bool ShenandoahGenerationalHeap::requires_barriers(stackChunkOop obj) const { return true; } - if (has_forwarded_objects()) { - // Object may have pointers that need to be updated - return true; - } - return false; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 0039bda9605b..8af8eae38000 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -73,6 +73,8 @@ #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahStackChunkGCData.inline.hpp" +#include "gc/shenandoah/shenandoahStackWatermark.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahUncommitThread.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" @@ -1231,7 +1233,24 @@ void ShenandoahHeap::evacuate_collection_set(ShenandoahGeneration* generation, b workers()->run_task(&task); } +class ShenandoahCompleteStackWatermarkHandshakeClosure : public HandshakeClosure { +public: + ShenandoahCompleteStackWatermarkHandshakeClosure() : HandshakeClosure("Shenandoah Complete stacks handshake") {} + void do_thread(Thread* thread) override { + if (thread->is_Java_thread()) { + JavaThread* jt = JavaThread::cast(thread); + StackWatermarkSet::finish_processing(jt, nullptr, StackWatermarkKind::gc); + } + } +}; + void ShenandoahHeap::concurrent_prepare_for_update_refs() { + // The stack watermarks are active since op_final_roots(). + // Make sure the current stack watermark machinery has completed before we drop evac flags. + // Otherwise the stack processing on stack unwinding may enter evac closure concurrently. + ShenandoahCompleteStackWatermarkHandshakeClosure cl; + Handshake::execute(&cl); + { // Java threads take this lock while they are being attached and added to the list of threads. // If another thread holds this lock before we update the gc state, it will receive a stale @@ -1256,24 +1275,32 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { _update_refs_iterator.reset(); } -void ShenandoahHeap::concurrent_final_roots() { - { - MutexLocker lock(Threads_lock); - +void ShenandoahHeap::op_final_roots() { #ifdef ASSERT + // Check if stack watermark machinery is in safe state: + // 1. With evac-in-progress, we are about to supersede evac processing with new epoch. + // op_thread_roots() should have completed the stack watermark processing before + // we reach here. + // 2. Without evac-in-progress, we are in abbreviated cycle, and we are switching + // to a new epoch that *also* does not change any oops, which is safe. + if (is_evacuation_in_progress()) { for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { StackWatermark* sw = StackWatermarkSet::get(jt, StackWatermarkKind::gc); assert(sw == nullptr || sw->processing_completed(), "Cannot turn off weak roots before stack watermark processing is complete"); } + } #endif - set_gc_state_concurrent(WEAK_ROOTS, false); - } + set_gc_state_at_safepoint(WEAK_ROOTS, false); + + // Arm the nmethods to change the barriers. + CodeCache::arm_all_nmethods(); - ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); - Threads::non_java_threads_do(&propagator); - Handshake::execute(&propagator); + { + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_roots_propagate_gc_state); + propagate_gc_state_to_all_threads(); + } } oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { @@ -2894,17 +2921,19 @@ void ShenandoahHeap::flush_liveness_cache(uint worker_id) { } bool ShenandoahHeap::requires_barriers(stackChunkOop obj) const { - if (is_idle()) return false; + // Can not guarantee obj is deeply good. + if (has_forwarded_objects()) { + return true; + } - // Objects allocated after marking start are implicitly alive, don't need any barriers during - // marking phase. + // Objects allocated after marking start are implicitly alive. if (is_concurrent_mark_in_progress() && - !marking_context()->allocated_after_mark_start(obj)) { + !marking_context()->allocated_after_mark_start(obj)) { return true; } - // Can not guarantee obj is deeply good. - if (has_forwarded_objects()) { + // Nmethods referenced by stack chunk may need the barrier fixups. + if (ShenandoahStackChunkGCData::is_different_epoch(obj)) { return true; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index bf84d60382e9..07d1f491dd6d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -495,7 +495,7 @@ class ShenandoahHeap : public CollectedHeap { void concurrent_prepare_for_update_refs(); // Turn off weak roots flag - void concurrent_final_roots(); + void op_final_roots(); virtual void update_heap_references(ShenandoahGeneration* generation, bool concurrent); // Final update region states diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp index 1d2cd97b75d1..c897cfe5b903 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp @@ -40,7 +40,7 @@ ShenandoahHeapRegionSet::ShenandoahHeapRegionSet() : _region_count(0) { // Use 1-byte data type - STATIC_ASSERT(sizeof(jbyte) == 1); + static_assert(sizeof(jbyte) == 1); // Initialize cset map Copy::zero_to_bytes(_set_map, _map_size); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp index 5318f38d8ef6..5722714cbdb3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp @@ -131,7 +131,7 @@ bool ShenandoahMarkBitMap::is_small_range_of_words(idx_t beg_full_word, idx_t en // because beg_full_word > end_full_word can occur when beg and end are in // the same word. // The threshold should be at least one word. - STATIC_ASSERT(small_range_words >= 1); + static_assert(small_range_words >= 1); return beg_full_word + small_range_words >= end_full_word; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp index ae56db810bbf..2f606da2f18a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp @@ -111,7 +111,7 @@ inline bool ShenandoahMarkBitMap::is_marked(HeapWord* addr) const { template inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_bit_impl(idx_t l_index, idx_t r_index) const { - STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); + static_assert(flip == find_ones_flip || flip == find_zeros_flip); verify_range(l_index, r_index); assert(!aligned_right || is_aligned(r_index, BitsPerWord), "r_index not aligned"); @@ -172,7 +172,7 @@ inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_bit_impl(idx_t template inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_prev_bit_impl(idx_t l_index, idx_t r_index) const { - STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); + static_assert(flip == find_ones_flip || flip == find_zeros_flip); verify_range(l_index, r_index); assert(!aligned_left || is_aligned(l_index, BitsPerWord), "l_index not aligned"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index b0573c3f677f..e18e9c48473c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2022, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -24,101 +24,126 @@ */ +#include "code/relocInfo.hpp" +#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/continuation.hpp" +#include "runtime/icache.hpp" #include "runtime/safepointVerifiers.hpp" -ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray& oops, bool non_immediate_oops) : - _nm(nm), _oops(nullptr), _oops_count(0), _unregistered(false), _lock(), _ic_lock() { - - if (!oops.is_empty()) { - _oops_count = oops.length(); - _oops = NEW_C_HEAP_ARRAY(oop*, _oops_count, mtGC); - for (int c = 0; c < _oops_count; c++) { - _oops[c] = oops.at(c); - } - } - _has_non_immed_oops = non_immediate_oops; - - assert_same_oops(); +ShenandoahNMethod::ShenandoahNMethod(nmethod* nm) : + _nm(nm), _oops(nullptr), _oops_count(0), _patchable_jumps(nullptr), _patchable_jumps_count(0), _unregistered(false), _lock(), _ic_lock() { + init_from(nm); } ShenandoahNMethod::~ShenandoahNMethod() { if (_oops != nullptr) { FREE_C_HEAP_ARRAY(_oops); } + if (_patchable_jumps != nullptr) { + FREE_C_HEAP_ARRAY(_patchable_jumps); + } } void ShenandoahNMethod::update() { + init_from(nm()); +} + +void ShenandoahNMethod::init_from(nmethod* nm) { ResourceMark rm; bool non_immediate_oops = false; GrowableArray oops; + GrowableArray jumps; + + parse(nm, oops, non_immediate_oops, jumps); - detect_reloc_oops(nm(), oops, non_immediate_oops); - if (oops.length() != _oops_count) { + int new_oops_count = oops.length(); + if (_oops_count != new_oops_count) { if (_oops != nullptr) { FREE_C_HEAP_ARRAY(_oops); _oops = nullptr; } - - _oops_count = oops.length(); - if (_oops_count > 0) { - _oops = NEW_C_HEAP_ARRAY(oop*, _oops_count, mtGC); + if (new_oops_count > 0) { + _oops = NEW_C_HEAP_ARRAY(oop*, new_oops_count, mtGC); } } + _oops_count = new_oops_count; + for (int c = 0; c < _oops_count; c++) { + _oops[c] = oops.at(c); + } + assert_same_oops(); - for (int index = 0; index < _oops_count; index ++) { - _oops[index] = oops.at(index); + int new_jumps_count = jumps.length(); + if (_patchable_jumps_count != new_jumps_count) { + if (_patchable_jumps != nullptr) { + FREE_C_HEAP_ARRAY(_patchable_jumps); + _patchable_jumps = nullptr; + } + if (new_jumps_count > 0) { + _patchable_jumps = NEW_C_HEAP_ARRAY(ShenandoahPatchableJump, new_jumps_count, mtGC); + } + } + _patchable_jumps_count = new_jumps_count; + for (int c = 0; c < _patchable_jumps_count; c++) { + _patchable_jumps[c] = jumps.at(c); } - _has_non_immed_oops = non_immediate_oops; - assert_same_oops(); + _has_non_immed_oops = non_immediate_oops; } -void ShenandoahNMethod::detect_reloc_oops(nmethod* nm, GrowableArray& oops, bool& has_non_immed_oops) { +void ShenandoahNMethod::parse(nmethod* nm, GrowableArray& oops, bool& has_non_immed_oops, GrowableArray& jumps) { has_non_immed_oops = false; - // Find all oops relocations + address code_begin = nm->code_begin(); RelocIterator iter(nm); while (iter.next()) { - if (iter.type() != relocInfo::oop_type) { - // Not an oop - continue; - } - - oop_Relocation* r = iter.oop_reloc(); - if (!r->oop_is_immediate()) { - // Non-immediate oop found - has_non_immed_oops = true; - continue; - } - - oop value = r->oop_value(); - if (value != nullptr) { - oop* addr = r->oop_addr(); - shenandoah_assert_correct(addr, value); - shenandoah_assert_not_in_cset_except(addr, value, ShenandoahHeap::heap()->cancelled_gc()); - shenandoah_assert_not_forwarded(addr, value); - // Non-null immediate oop found. null oops can safely be - // ignored since the method will be re-registered if they - // are later patched to be non-null. - oops.push(addr); + switch (iter.type()) { + case relocInfo::oop_type: { + oop_Relocation* r = iter.oop_reloc(); + if (!r->oop_is_immediate()) { + // Non-immediate oop found + has_non_immed_oops = true; + break; + } + + oop value = r->oop_value(); + if (value != nullptr) { + oop* addr = r->oop_addr(); + shenandoah_assert_correct(addr, value); + shenandoah_assert_not_in_cset_except(addr, value, ShenandoahHeap::heap()->cancelled_gc()); + shenandoah_assert_not_forwarded(addr, value); + // Non-null immediate oop found. null oops can safely be + // ignored since the method will be re-registered if they + // are later patched to be non-null. + oops.push(addr); + } + break; + } + case relocInfo::patchable_barrier_type: { + patchable_barrier_Relocation* r = iter.patchable_barrier_reloc(); + + ShenandoahPatchableJump b; + b._rel_pc = checked_cast(pointer_delta(r->addr(), code_begin, 1)); + b._rel_target_pc = r->target_offset(); + b._gc_state = decode_reloc_gc_state(r->metadata()); + b._jump_when_state = decode_reloc_jump_when_state(r->metadata()); + jumps.push(b); + break; + } + default: + // We do not care about other relocations. + break; } } } ShenandoahNMethod* ShenandoahNMethod::for_nmethod(nmethod* nm) { - ResourceMark rm; - bool non_immediate_oops = false; - GrowableArray oops; - - detect_reloc_oops(nm, oops, non_immediate_oops); - return new ShenandoahNMethod(nm, oops, non_immediate_oops); + return new ShenandoahNMethod(nm); } -void ShenandoahNMethod::heal_nmethod(nmethod* nm) { +void ShenandoahNMethod::handle_oops(nmethod* nm, ICacheInvalidationContext* icic) { ShenandoahNMethod* data = gc_data(nm); assert(data != nullptr, "Sanity"); assert(data->lock()->owned_by_self(), "Must hold the lock"); @@ -126,17 +151,72 @@ void ShenandoahNMethod::heal_nmethod(nmethod* nm) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); if ((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || heap->is_concurrent_strong_root_in_progress()) { - heal_nmethod_metadata(data); + heal_nmethod_metadata(data, icic); } else if (heap->is_concurrent_mark_in_progress()) { ShenandoahKeepAliveClosure cl; - data->oops_do(&cl); + data->oops_do(&cl, /* fix_relocations = */ false, /* icic = */ nullptr); } else { - // There is possibility that GC is cancelled when it arrives final mark. - // In this case, concurrent root phase is skipped and degenerated GC should be - // followed, where nmethods are disarmed. + // No other phases need oop fixups or scans. } } +void ShenandoahNMethod::handle_jumps(nmethod* nm, ICacheInvalidationContext* icic) { + ShenandoahNMethod* data = gc_data(nm); + assert(data != nullptr, "Sanity"); + assert(data->lock()->owned_by_self(), "Must hold the lock"); + + char gc_state = ShenandoahHeap::heap()->gc_state(); + address code_begin = nm->code_begin(); + + bool changed = false; + for (int c = 0; c < data->_patchable_jumps_count; c++) { + ShenandoahPatchableJump& b = data->_patchable_jumps[c]; + changed |= patch_jump(code_begin + b._rel_pc, + code_begin + b._rel_target_pc, + ((gc_state & b._gc_state) != 0) == b._jump_when_state); + } + if (changed) { + icic->set_has_modified_code(); + } +} + +// Use precise instruction rewrite code, and only when it recognizes the current insns. +// +// This patching code is non-atomic, but it runs in two safe contexts: +// a) For new nmethods that are not yet executing and not yet live. This covers the paths +// for newly compiled methods, nmethods that were just relocated, the nmethods that +// were AOT-loaded. +// b) For existing methods in the nmethod entry barrier context. The nmethod entry barriers +// are armed along with stack watermark machinery activation. Together they guarantee +// the nmethod updates are not interleaved with execution, and nmethod would be patched +// before allowing to proceed. +// +// The icache flushing is also handled on both paths. +// +bool ShenandoahNMethod::patch_jump(address pc, address target_pc, bool should_jump) { + bool patched = true; + if (should_jump && ShenandoahBarrierSetAssembler::is_patchable_nop(pc)) { + ShenandoahBarrierSetAssembler::insert_patchable_jump(pc, target_pc); + } else if (!should_jump && ShenandoahBarrierSetAssembler::is_patchable_jump(pc, target_pc)) { + ShenandoahBarrierSetAssembler::insert_patchable_nop(pc); + } else { + patched = false; + } + + // Failing to change the jump is catastrophic for correctness, + // so prefer to crash hard even in product. + if (should_jump) { + guarantee(ShenandoahBarrierSetAssembler::is_patchable_jump(pc, target_pc), + "Should be jump to the same address"); + assert(ShenandoahBarrierSetAssembler::parse_jump_address(pc) == target_pc, + "Cross-checking, jump should be to the same address"); + } else { + guarantee(ShenandoahBarrierSetAssembler::is_patchable_nop(pc), + "Should be patchable nop"); + } + return patched; +} + #ifdef ASSERT void ShenandoahNMethod::assert_correct() { ShenandoahHeap* heap = ShenandoahHeap::heap(); @@ -205,8 +285,9 @@ void ShenandoahNMethod::assert_same_oops() { debug_stream.print_cr("-> " PTR_FORMAT, p2i(_oops[i])); } GrowableArray check; + GrowableArray jumps; bool non_immed; - detect_reloc_oops(nm(), check, non_immed); + parse(nm(), check, non_immed, jumps); debug_stream.print_cr("check oops: %d", check.length()); for (int i = 0; i < check.length(); i++) { debug_stream.print_cr("-> " PTR_FORMAT, p2i(check.at(i))); @@ -219,6 +300,7 @@ void ShenandoahNMethod::assert_same_oops() { ShenandoahNMethodTable::ShenandoahNMethodTable() : _heap(ShenandoahHeap::heap()), + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()), _index(0), _itr_cnt(0) { _list = new ShenandoahNMethodList(minSize); @@ -236,24 +318,37 @@ void ShenandoahNMethodTable::register_nmethod(nmethod* nm) { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(nm); if (data != nullptr) { + // Re-registering the existing nmethod. This is the C1 oop patching path. + // We expect no patchable jumps here, as only oops can change in C1 case. assert(contain(nm), "Must have been registered"); assert(nm == data->nm(), "Must be same nmethod"); + assert(nm->is_compiled_by_c1(), "Must be compiled by C1"); + assert(!data->has_patchable_jumps(), "Must not have patchable jumps"); // Prevent updating a nmethod while concurrent iteration is in progress. wait_until_concurrent_iteration_done(); ShenandoahNMethodLocker data_locker(data->lock()); data->update(); } else { - // For a new nmethod, we can safely append it to the list, because - // concurrent iteration will not touch it. + // New nmethod, not yet executing. We can safely append it to the list, + // because concurrent iteration will not touch it. Ditto we do jump + // fixups right here, without relying on nmethod entry barrier to be armed + // for new nmethods. data = ShenandoahNMethod::for_nmethod(nm); assert(data != nullptr, "Sanity"); ShenandoahNMethod::attach_gc_data(nm, data); ShenandoahLocker locker(&_lock); log_register_nmethod(nm); append(data); + ShenandoahNMethodLocker data_locker(data->lock()); + { + ICacheInvalidationContext icic; + ShenandoahNMethod::handle_jumps(nm, &icic); + } + ShenandoahNMethod::disarm_nmethod(nm); } - // Disarm new nmethod - ShenandoahNMethod::disarm_nmethod(nm); + + assert(!data->has_patchable_jumps() || _bs_nm->supports_entry_barrier(nm), + "NMethods with patchable jumps require entry barrier support"); } void ShenandoahNMethodTable::unregister_nmethod(nmethod* nm) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 2686b4f49858..04c6bf0a804c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -33,10 +33,19 @@ #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" +class BarrierSetNMethod; + // Use ShenandoahReentrantLock as ShenandoahNMethodLock typedef ShenandoahReentrantLock ShenandoahNMethodLock; typedef ShenandoahLocker ShenandoahNMethodLocker; +struct ShenandoahPatchableJump { + int32_t _rel_pc; + int32_t _rel_target_pc; + char _gc_state; + bool _jump_when_state; +}; + // ShenandoahNMethod tuple records the internal locations of oop slots within reclocation stream in // the nmethod. This allows us to quickly scan the oops without doing the nmethod-internal scans, // that sometimes involves parsing the machine code. Note it does not record the oops themselves, @@ -46,19 +55,36 @@ class ShenandoahNMethod : public CHeapObj { nmethod* const _nm; oop** _oops; int _oops_count; + ShenandoahPatchableJump* _patchable_jumps; + int _patchable_jumps_count; bool _has_non_immed_oops; bool _unregistered; ShenandoahNMethodLock _lock; ShenandoahNMethodLock _ic_lock; public: - ShenandoahNMethod(nmethod *nm, GrowableArray& oops, bool has_non_immed_oops); + ShenandoahNMethod(nmethod *nm); ~ShenandoahNMethod(); + static bool decode_reloc_jump_when_state(uint16_t reloc) { + return (reloc & (1 << 8)) != 0; + } + + static char decode_reloc_gc_state(uint16_t reloc) { + return (reloc & 0xFF); + } + + static uint16_t encode_to_reloc(char gc_state, bool jump_when_state) { + uint16_t res = (gc_state & 0xFF) | (jump_when_state ? (1 << 8) : 0); + assert(decode_reloc_jump_when_state(res) == jump_when_state, "Round-trip"); + assert(decode_reloc_gc_state(res) == gc_state, "Round-trip"); + return res; + } + inline nmethod* nm() const; inline ShenandoahNMethodLock* lock(); inline ShenandoahNMethodLock* ic_lock(); - inline void oops_do(OopClosure* oops, bool fix_relocations = false); + inline void oops_do(OopClosure* oops, bool fix_relocations, ICacheInvalidationContext* icic); // Update oops when the nmethod is re-registered void update(); @@ -68,8 +94,9 @@ class ShenandoahNMethod : public CHeapObj { static inline ShenandoahNMethodLock* lock_for_nmethod(nmethod* nm); static inline ShenandoahNMethodLock* ic_lock_for_nmethod(nmethod* nm); - static void heal_nmethod(nmethod* nm); - static inline void heal_nmethod_metadata(ShenandoahNMethod* nmethod_data); + static void handle_oops(nmethod* nm, ICacheInvalidationContext* icic); + static void handle_jumps(nmethod* nm, ICacheInvalidationContext* icic); + static inline void heal_nmethod_metadata(ShenandoahNMethod* nmethod_data, ICacheInvalidationContext* icic); static inline void disarm_nmethod(nmethod* nm); static inline ShenandoahNMethod* gc_data(nmethod* nm); @@ -78,8 +105,14 @@ class ShenandoahNMethod : public CHeapObj { void assert_correct() NOT_DEBUG_RETURN; void assert_same_oops() NOT_DEBUG_RETURN; + bool has_patchable_jumps() { + return _patchable_jumps_count > 0; + } + private: - static void detect_reloc_oops(nmethod* nm, GrowableArray& oops, bool& _has_non_immed_oops); + void init_from(nmethod* nm); + static void parse(nmethod* nm, GrowableArray& oops, bool& _has_non_immed_oops, GrowableArray& jumps); + static bool patch_jump(address pc, address target_pc, bool should_jump); }; class ShenandoahNMethodTable; @@ -139,6 +172,8 @@ class ShenandoahNMethodTable : public CHeapObj { }; ShenandoahHeap* const _heap; + BarrierSetNMethod* const _bs_nm; + ShenandoahNMethodList* _list; int _index; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp index ef9e347b821d..b2fca17afa2e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.inline.hpp @@ -47,7 +47,9 @@ bool ShenandoahNMethod::is_unregistered() const { return _unregistered; } -void ShenandoahNMethod::oops_do(OopClosure* oops, bool fix_relocations) { +void ShenandoahNMethod::oops_do(OopClosure* oops, bool fix_relocations, ICacheInvalidationContext* icic) { + assert(!fix_relocations || icic != nullptr, "Need ICIC if fixing relocations"); + for (int c = 0; c < _oops_count; c ++) { oops->do_oop(_oops[c]); } @@ -60,14 +62,21 @@ void ShenandoahNMethod::oops_do(OopClosure* oops, bool fix_relocations) { } } - if (fix_relocations && _has_non_immed_oops) { - _nm->fix_oop_relocations(); + if (fix_relocations) { + // If we have immediate oops, tracked by us, it is likely we have modified them. + // Declare this fact to ICIC. + if (_oops_count > 0) { + icic->set_has_modified_code(); + } + if (_has_non_immed_oops) { + _nm->fix_oop_relocations(icic); + } } } -void ShenandoahNMethod::heal_nmethod_metadata(ShenandoahNMethod* nmethod_data) { +void ShenandoahNMethod::heal_nmethod_metadata(ShenandoahNMethod* nmethod_data, ICacheInvalidationContext* icic) { ShenandoahEvacuateUpdateMetadataClosure cl; - nmethod_data->oops_do(&cl, true /*fix relocation*/); + nmethod_data->oops_do(&cl, /* fix_relocations = */ true, icic); } void ShenandoahNMethod::disarm_nmethod(nmethod* nm) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index 1ae080d31c64..2b813560d6b6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -22,6 +22,7 @@ * */ +#include "code/codeCache.hpp" #include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" @@ -73,6 +74,9 @@ void ShenandoahOldGC::op_final_mark() { Universe::verify(); } + // Arm nmethods/stack for concurrent processing + CodeCache::arm_all_nmethods(); + { ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::final_mark_propagate_gc_state); heap->propagate_gc_state_to_all_threads(); @@ -138,7 +142,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // return from here with weak roots in progress. This is not a valid gc state // for any young collections (or allocation failures) that interrupt the old // collection. - entry_final_roots(); + vmop_entry_final_roots(); // After concurrent old marking finishes, we reclaim immediate garbage. Further, we may also want to expand OLD in order // to make room for anticipated promotions and/or for mixed evacuations. Mixed evacuations are especially likely to diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index 0eb5a1e6092d..68c568dcc1dd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -105,10 +105,12 @@ class outputStream; SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_class_unload_purge_ec, " Exception Caches") \ SHENANDOAH_WORKER_PHASE_DEF(f, conc_strong_roots, "Concurrent Strong Roots", \ " CSR: ") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, final_roots_gross, "Pause Final Roots (G)") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, final_roots, "Pause Final Roots (N)") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, final_roots_propagate_gc_state, " Propagate GC State") \ SHENANDOAH_WORKER_PHASE_DEF(f, conc_evac, "Concurrent Evacuation", \ " CE: ") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_update_card_table, "Concurrent Update Cards") \ - SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_final_roots, "Concurrent Final Roots") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated, "Complete Abbreviated Cycle") \ SHENANDOAH_WORKER_PHASE_DEF(f, complete_abbreviated_promote_in_place, " Promote Regions", \ " PIP: ") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index 91f6f605a84b..1b7d0b75304e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -199,7 +199,6 @@ ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTi void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { NMethodToOopClosure code_blob_cl(oops, NMethodToOopClosure::FixRelocations); - ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(oops); CLDToOopClosure adjust_cld_closure(oops, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -208,7 +207,7 @@ void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { _cld_roots.cld_do(&adjust_cld_closure, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); + _code_roots.nmethods_do(&code_blob_cl, worker_id); _thread_roots.oops_do(oops, nullptr, worker_id); } @@ -239,7 +238,7 @@ class ShenandoahMarkNMethodClosure : public NMethodClosure { } ShenandoahNMethod* const snm = ShenandoahNMethod::gc_data(nm); assert(snm != nullptr, "Sanity"); - snm->oops_do(_oops, false /*fix_relocations*/); + snm->oops_do(_oops, /* fix_relocations = */ false, /* icic = */ nullptr); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp index 4694a1e05194..13ce0fc20715 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp @@ -213,8 +213,7 @@ void ShenandoahSTWRootScanner::roots_do(T* oops, uint worker_id) { template void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive) { - NMethodToOopClosure update_nmethods(keep_alive, NMethodToOopClosure::FixRelocations); - ShenandoahNMethodAndDisarmClosure nmethods_and_disarm_Cl(keep_alive); + NMethodToOopClosure nmethods_cl(keep_alive, NMethodToOopClosure::FixRelocations); CLDToOopClosure clds(keep_alive, ClassLoaderData::_claim_strong); // Process light-weight/limited parallel roots then @@ -223,7 +222,7 @@ void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAliv _cld_roots.cld_do(&clds, worker_id); // Process heavy-weight/fully parallel roots the last - _code_roots.nmethods_do(&nmethods_and_disarm_Cl, worker_id); + _code_roots.nmethods_do(&nmethods_cl, worker_id); _thread_roots.oops_do(keep_alive, nullptr, worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp index d482c83dd802..290c0f389629 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSTWMark.cpp @@ -24,6 +24,7 @@ */ +#include "code/codeCache.hpp" #include "code/nmethod.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shared/workerThread.hpp" @@ -74,7 +75,7 @@ void ShenandoahSTWMark::mark() { // Arm all nmethods. Even though this is STW mark, some marking code // piggybacks on nmethod barriers for special instances. - ShenandoahCodeRoots::arm_nmethods(); + CodeCache::arm_all_nmethods(); // Weak reference processing ShenandoahReferenceProcessor* rp = _generation->ref_processor(); @@ -115,9 +116,6 @@ void ShenandoahSTWMark::mark() { _generation->set_mark_complete(); end_mark(); - // Mark is finished, can disarm the nmethods now. - ShenandoahCodeRoots::disarm_nmethods(); - assert(task_queues()->is_empty(), "Should be empty"); TASKQUEUE_STATS_ONLY(task_queues()->print_and_reset_taskqueue_stats("")); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index 989c0b9c0fd5..2c60d4340f51 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -369,7 +369,7 @@ class ShenandoahCardCluster: public CHeapObj { static const uint8_t FirstStartBits = 0x7f; // Check that we have enough bits to store the largest possible offset into a card for an object start. - STATIC_ASSERT((MaxGCCardSizeInBytes / HeapWordSize) - 1 <= FirstStartBits); + static_assert((MaxGCCardSizeInBytes / HeapWordSize) - 1 <= FirstStartBits); crossing_info* _object_starts; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.cpp new file mode 100644 index 000000000000..0c66d54a225a --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.cpp @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "gc/shenandoah/shenandoahAsserts.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahStackChunkGCData.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" +#include "runtime/atomic.hpp" + +Atomic ShenandoahStackChunkGCData::_epoch_id_counter; + +void ShenandoahStackChunkGCData::change_epoch_id() { + shenandoah_assert_safepoint(); + _epoch_id_counter.add_then_fetch(1, memory_order_relaxed); +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.hpp new file mode 100644 index 000000000000..99ed3b61aed8 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.hpp @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_HPP + +#include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" + +class ShenandoahStackChunkGCData { +private: + // Monotonically increasing epoch counter. + // Must not wrap to track the epoch accurately. + static Atomic _epoch_id_counter; + + // The GC epoch when chunk was allocated. + int64_t _epoch; + + static ShenandoahStackChunkGCData* data(stackChunkOop chunk); + +public: + static void change_epoch_id(); + + static void initialize(stackChunkOop chunk); + static bool is_different_epoch(stackChunkOop chunk); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.inline.hpp new file mode 100644 index 000000000000..44ba4bb6a095 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackChunkGCData.inline.hpp @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_INLINE_HPP + +#include "gc/shenandoah/shenandoahStackChunkGCData.hpp" + +#include "oops/instanceStackChunkKlass.inline.hpp" +#include "oops/stackChunkOop.inline.hpp" + +inline ShenandoahStackChunkGCData* ShenandoahStackChunkGCData::data(stackChunkOop chunk) { +#ifdef ASSERT + size_t data_size = sizeof(ShenandoahStackChunkGCData); + size_t avail_size = InstanceStackChunkKlass::gc_data_size(chunk->stack_size()) * HeapWordSize; + assert(data_size <= avail_size, "Data of size %zu must fit in %zu space", data_size, avail_size); +#endif + return reinterpret_cast(chunk->gc_data()); +} + +inline void ShenandoahStackChunkGCData::initialize(stackChunkOop chunk) { + data(chunk)->_epoch = _epoch_id_counter.load_relaxed(); +} + +inline bool ShenandoahStackChunkGCData::is_different_epoch(stackChunkOop chunk) { + return data(chunk)->_epoch != _epoch_id_counter.load_relaxed(); +} + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTACKCHUNKGCDATA_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp index dac64629bce6..dbed4d259887 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -64,6 +64,7 @@ ShenandoahStackWatermark::ShenandoahStackWatermark(JavaThread* jt) : StackWatermark(jt, StackWatermarkKind::gc, _epoch_id), _heap(ShenandoahHeap::heap()), _stats(), + _no_op_cl(), _keep_alive_cl(), _evac_update_oop_cl(), _nm_cl() { @@ -73,9 +74,6 @@ ShenandoahStackWatermark::ShenandoahStackWatermark(JavaThread* jt) : OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { if (context != nullptr) { - assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || - _heap->is_concurrent_mark_in_progress(), - "Only these two phases"); assert(Thread::current()->is_Worker_thread(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); return reinterpret_cast(context); } else { @@ -84,8 +82,7 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { } else if (_heap->is_concurrent_mark_in_progress()) { return &_keep_alive_cl; } else { - ShouldNotReachHere(); - return nullptr; + return &_no_op_cl; } } } @@ -93,27 +90,22 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { void ShenandoahStackWatermark::start_processing_impl(void* context) { NoSafepointVerifier nsv; - // Process the non-frame part of the thread if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { - // Retire the TLABs, which will force threads to reacquire their TLABs. // This is needed for two reasons. Strong one: new allocations would be with new freeset, // which would be outside the collection set, so no cset writes would happen there. // Weaker one: new allocations would happen past update watermark, and so less work would // be needed for reference updates (would update the large filler instead). retire_tlab(); - - _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); } else if (_heap->is_concurrent_mark_in_progress()) { // We need to reset all TLABs because they might be below the TAMS, and we need to mark // the objects in them. Do not let mutators allocate any new objects in their current TLABs. // It is also a good place to resize the TLAB sizes for future allocations. retire_tlab(); - - _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); - } else { - ShouldNotReachHere(); } + // Process the non-frame part of the thread + _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); + // Publishes the processing start to concurrent threads StackWatermark::start_processing_impl(context); } @@ -132,8 +124,5 @@ void ShenandoahStackWatermark::retire_tlab() { void ShenandoahStackWatermark::process(const frame& fr, RegisterMap& register_map, void* context) { OopClosure* oops = closure_from_context(context); assert(oops != nullptr, "Should not get to here"); - assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || - _heap->is_concurrent_mark_in_progress(), - "Only these two phases"); fr.oops_do(oops, &_nm_cl, ®ister_map, DerivedPointerIterationMode::_directly); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.hpp index 78aba0b569e2..ce87050a8b70 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, Red Hat, Inc. All rights reserved. - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,6 +60,7 @@ class ShenandoahStackWatermark : public StackWatermark { ThreadLocalAllocStats _stats; // Closures + ShenandoahNoOpClosure _no_op_cl; ShenandoahKeepAliveClosure _keep_alive_cl; ShenandoahEvacuateUpdateRootsClosure _evac_update_oop_cl; ShenandoahOnStackNMethodClosure _nm_cl; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index a7d553e1208e..48bdd07b2570 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -171,7 +171,7 @@ class ShenandoahMarkTask static const int pow_range_mask = right_n_bits(pow_bits); inline oop decode_oop(uintptr_t val) const { - STATIC_ASSERT(oop_shift == 0); + static_assert(oop_shift == 0); return cast_to_oop(val & oop_extract_mask); } @@ -197,7 +197,7 @@ class ShenandoahMarkTask } inline uintptr_t encode_oop(oop obj, bool skip_live, bool weak) const { - STATIC_ASSERT(oop_shift == 0); + static_assert(oop_shift == 0); uintptr_t encoded = cast_from_oop(obj); if (skip_live) { encoded |= skip_live_extract_mask; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index d7d9e579b588..1a42e0e7b113 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -31,7 +31,6 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _gc_state(0), - _gc_state_fast_array{}, _satb_mark_queue(&ShenandoahBarrierSet::satb_mark_queue_set()), _card_table(nullptr), _gclab(nullptr), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index da8f060db6ea..60dcd2528f7f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -47,31 +47,6 @@ class ShenandoahThreadLocalData { // Thread-local mirror for global GC state char _gc_state; - // Quickened version of GC state. - // This allows all architectures to quickly check the group of states by using a single byte load. - enum FastGCState { - FORWARDED = ShenandoahHeap::HAS_FORWARDED, - MARKING = ShenandoahHeap::MARKING, - WEAK = ShenandoahHeap::WEAK_ROOTS, - FORWARDED_MARKING = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING, - FORWARDED_WEAK = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::WEAK_ROOTS, - MARKING_WEAK = ShenandoahHeap::MARKING | ShenandoahHeap::WEAK_ROOTS, - FORWARDED_MARKING_WEAK = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING | ShenandoahHeap::WEAK_ROOTS - }; - - enum FastGCStatePos { - POS_FORWARDED = 0, - POS_MARKING = 1, - POS_WEAK = 2, - POS_FORWARDED_MARKING = 3, - POS_FORWARDED_WEAK = 4, - POS_MARKING_WEAK = 5, - POS_FORWARDED_MARKING_WEAK = 6, - POS_MAX - }; - - char _gc_state_fast_array[POS_MAX]; - SATBMarkQueue _satb_mark_queue; // Current active CardTable's byte_map_base for this thread. @@ -124,28 +99,9 @@ class ShenandoahThreadLocalData { return data(thread)->_satb_mark_queue; } - static char gc_state_to_fast_array_index(char gc_state) { - if (gc_state == FORWARDED) return POS_FORWARDED; - if (gc_state == MARKING) return POS_MARKING; - if (gc_state == WEAK) return POS_WEAK; - if (gc_state == FORWARDED_MARKING) return POS_FORWARDED_MARKING; - if (gc_state == FORWARDED_WEAK) return POS_FORWARDED_WEAK; - if (gc_state == MARKING_WEAK) return POS_MARKING_WEAK; - if (gc_state == FORWARDED_MARKING_WEAK) return POS_FORWARDED_MARKING_WEAK; - ShouldNotReachHere(); - return 0; - } - static void set_gc_state(Thread* thread, char gc_state) { ShenandoahThreadLocalData* d = data(thread); d->_gc_state = gc_state; - d->_gc_state_fast_array[POS_FORWARDED] = (gc_state & FORWARDED) != 0; - d->_gc_state_fast_array[POS_MARKING] = (gc_state & MARKING) != 0; - d->_gc_state_fast_array[POS_WEAK] = (gc_state & WEAK) != 0; - d->_gc_state_fast_array[POS_FORWARDED_MARKING] = (gc_state & FORWARDED_MARKING) != 0; - d->_gc_state_fast_array[POS_FORWARDED_WEAK] = (gc_state & FORWARDED_WEAK) != 0; - d->_gc_state_fast_array[POS_MARKING_WEAK] = (gc_state & MARKING_WEAK) != 0; - d->_gc_state_fast_array[POS_FORWARDED_MARKING_WEAK] = (gc_state & FORWARDED_MARKING_WEAK) != 0; } static char gc_state(Thread* thread) { @@ -222,10 +178,6 @@ class ShenandoahThreadLocalData { return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _gc_state); } - static ByteSize gc_state_fast_array_offset(char gc_state) { - return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _gc_state_fast_array) + in_ByteSize(gc_state_to_fast_array_index(gc_state)); - } - static ByteSize card_table_offset() { return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _card_table); } @@ -266,6 +218,6 @@ class ShenandoahThreadLocalData { } }; -STATIC_ASSERT(sizeof(ShenandoahThreadLocalData) <= sizeof(GCThreadLocalData)); +static_assert(sizeof(ShenandoahThreadLocalData) <= sizeof(GCThreadLocalData)); #endif // SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index ac7fe1f9a3aa..962935b57997 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -82,7 +82,7 @@ class ShenandoahIsUnloadingBehaviour : public IsUnloadingBehaviour { ShenandoahNMethod* data = ShenandoahNMethod::gc_data(nm); ShenandoahNMethodLocker locker(data->lock()); ShenandoahIsUnloadingOopClosure cl; - data->oops_do(&cl); + data->oops_do(&cl, /* fix_relocations = */ false, /* icic = */ nullptr); return cl.is_unloading(); } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index e16801871c58..c37d720983b5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -223,6 +223,7 @@ class ShenandoahSafepoint : public AllStatic { VM_Operation::VMOp_Type type = vm_op->type(); return type == VM_Operation::VMOp_ShenandoahInitMark || type == VM_Operation::VMOp_ShenandoahFinalMarkStartEvac || + type == VM_Operation::VMOp_ShenandoahFinalRoots || type == VM_Operation::VMOp_ShenandoahInitUpdateRefs || type == VM_Operation::VMOp_ShenandoahFinalUpdateRefs || type == VM_Operation::VMOp_ShenandoahFinalVerify || diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index 97dd7e5cda1a..62ad794edc84 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -135,6 +135,16 @@ void VM_ShenandoahFinalUpdateRefs::doit() { _gc->entry_final_update_refs(); } +VM_ShenandoahFinalRoots::VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc) + : VM_ShenandoahOperation(gc->generation()), _gc(gc) { +} + +void VM_ShenandoahFinalRoots::doit() { + ShenandoahGCPauseMark mark(_gc_id, "Final Roots", SvcGCMarker::CONCURRENT); + set_active_generation(); + _gc->entry_final_roots(); +} + VM_ShenandoahFinalVerify::VM_ShenandoahFinalVerify(ShenandoahConcurrentGC* gc) : VM_ShenandoahOperation(gc->generation()), _gc(gc) { } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp index f8b99c71b14c..d9d3ca90f40b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.hpp @@ -36,6 +36,7 @@ class ShenandoahFullGC; // VM_ShenandoahOperation // - VM_ShenandoahInitMark: initiate concurrent marking // - VM_ShenandoahFinalMarkStartEvac: finish up concurrent marking, and start evacuation +// - VM_ShenandoahFinalRoots: finish up roots processing // - VM_ShenandoahInitUpdateRefs: initiate update references // - VM_ShenandoahFinalUpdateRefs: finish up update references // - VM_ShenandoahFinalVerify: final verification at the end of the cycle @@ -127,6 +128,15 @@ class VM_ShenandoahFinalUpdateRefs: public VM_ShenandoahOperation { void doit() override; }; +class VM_ShenandoahFinalRoots: public VM_ShenandoahOperation { + ShenandoahConcurrentGC* const _gc; +public: + explicit VM_ShenandoahFinalRoots(ShenandoahConcurrentGC* gc); + VM_Operation::VMOp_Type type() const override { return VMOp_ShenandoahFinalRoots; } + const char* name() const override { return "Shenandoah Final Roots"; } + void doit() override; +}; + class VM_ShenandoahFinalVerify: public VM_ShenandoahOperation { ShenandoahConcurrentGC* const _gc; public: diff --git a/src/hotspot/share/gc/z/zMappedCache.cpp b/src/hotspot/share/gc/z/zMappedCache.cpp index a9336fddafd2..dcdc0c9e0d40 100644 --- a/src/hotspot/share/gc/z/zMappedCache.cpp +++ b/src/hotspot/share/gc/z/zMappedCache.cpp @@ -90,7 +90,7 @@ ZMappedCacheEntry* ZMappedCacheEntry::cast_to_entry(ZMappedCache::SizeClassListN } static void* entry_address_for_zoffset_end(zoffset_end offset) { - STATIC_ASSERT(is_aligned(ZCacheLineSize, alignof(ZMappedCacheEntry)));; + static_assert(is_aligned(ZCacheLineSize, alignof(ZMappedCacheEntry))); // This spreads out the location of the entries in an effort to combat hyper alignment. // Verify if this is an efficient and worthwhile optimization. diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 2779151c576b..404382fca7f3 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -29,8 +29,9 @@ #include "oops/accessDecorators.hpp" #include "oops/oopsHierarchy.hpp" -class nmethod; +class ICacheInvalidationContext; class NMethodClosure; +class nmethod; class ZReentrantLock; class ZWorkers; diff --git a/src/hotspot/share/jfr/leakprofiler/utilities/unifiedOopRef.hpp b/src/hotspot/share/jfr/leakprofiler/utilities/unifiedOopRef.hpp index dad0a7cd08f8..2000ca7fb928 100644 --- a/src/hotspot/share/jfr/leakprofiler/utilities/unifiedOopRef.hpp +++ b/src/hotspot/share/jfr/leakprofiler/utilities/unifiedOopRef.hpp @@ -33,10 +33,10 @@ struct UnifiedOopRef { static const uintptr_t native_tag = 0b001; static const uintptr_t raw_tag = 0b010; static const uintptr_t narrow_tag = LP64_ONLY(0b100) NOT_LP64(0); - STATIC_ASSERT((native_tag & raw_tag) == 0); - STATIC_ASSERT((native_tag & narrow_tag) == 0); - STATIC_ASSERT((raw_tag & narrow_tag) == 0); - STATIC_ASSERT((native_tag | raw_tag | narrow_tag) == tag_mask); + static_assert((native_tag & raw_tag) == 0); + static_assert((native_tag & narrow_tag) == 0); + static_assert((raw_tag & narrow_tag) == 0); + static_assert((native_tag | raw_tag | narrow_tag) == tag_mask); uintptr_t _value; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp index ddbfebb15984..0c40d0f5ef08 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -219,11 +219,11 @@ static bool register_static_type(JfrTypeId id, bool permit_cache, JfrSerializer* delete serializer; return false; } + SerializerRegistrationGuard guard; if (JfrRecorder::is_recording()) { JfrCheckpointWriter writer(Thread::current(), true, STATICS, JFR_THREADLOCAL); registration->invoke(writer); } - SerializerRegistrationGuard guard; assert(!types.in_list(registration), "invariant"); DEBUG_ONLY(assert_not_registered_twice(id, types);) types.add(registration); diff --git a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp index e5420d29d945..99d55ca65615 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Datadog, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,7 @@ #include "jfr/recorder/service/jfrEventThrottler.hpp" #include "jfrfiles/jfrEventIds.hpp" #include "logging/log.hpp" +#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/spinCriticalSection.hpp" @@ -45,7 +46,7 @@ constexpr static int num_throttled_events = sizeof(throttleble_events) / sizeof( // Throttler-by-ID lookup table class ThrottlerLookupTable { static constexpr int max = (int)LAST_EVENT_ID; - STATIC_ASSERT(max < 1000); // should this ever get unreasonably large, we rethink this table. + static_assert(max < 1000); // should this ever get unreasonably large, we rethink this table. JfrEventThrottler* _table[max]; public: ThrottlerLookupTable() { memset(_table, 0, sizeof(_table)); } @@ -89,7 +90,7 @@ JfrEventThrottler::JfrEventThrottler(JfrEventId event_id) : bool JfrEventThrottler::create() { bool rc = _throttler_table.initialize(); if (rc) { - _throttler_table.at(JfrCPUTimeSampleEvent)->_disabled = true; // CPU time sampler disabled + AtomicAccess::store(&_throttler_table.at(JfrCPUTimeSampleEvent)->_disabled, true); // CPU time sampler disabled } return rc; } @@ -139,7 +140,7 @@ void JfrEventThrottler::configure(int64_t sample_size, int64_t period_ms) { bool JfrEventThrottler::accept(JfrEventId event_id, int64_t timestamp /* 0 */) { JfrEventThrottler* const throttler = for_event(event_id); assert(throttler != nullptr, "invariant"); - return throttler->_disabled ? true : throttler->sample(timestamp); + return AtomicAccess::load(&throttler->_disabled) ? true : throttler->sample(timestamp); } /* @@ -243,8 +244,9 @@ inline bool is_disabled(int64_t event_sample_size) { } const JfrSamplerParams& JfrEventThrottler::update_params(const JfrSamplerWindow* expired) { - _disabled = is_disabled(_sample_size); - if (_disabled) { + const bool disabled = is_disabled(_sample_size); + AtomicAccess::store(&_disabled, disabled); + if (disabled) { return _disabled_params; } normalize(&_sample_size, &_period_ms); @@ -317,5 +319,5 @@ const JfrSamplerParams& JfrEventThrottler::next_window_params(const JfrSamplerWi if (_update) { return update_params(expired); // Updates _last_params in-place. } - return _disabled ? _disabled_params : _last_params; + return AtomicAccess::load(&_disabled) ? _disabled_params : _last_params; } diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp index d403a38703ec..95b30b86a920 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackTraceRepository.cpp @@ -212,6 +212,7 @@ traceid JfrStackTraceRepository::add_trace(const JfrStackTrace& stacktrace) { // invariant is that the entry to be resolved actually exists in the table const JfrStackTrace* JfrStackTraceRepository::lookup_for_leak_profiler(traceid hash, traceid id) { + MutexLocker lock(JfrStacktrace_lock, Mutex::_no_safepoint_check_flag); const size_t index = (hash % TABLE_SIZE); const JfrStackTrace* trace = leak_profiler_instance()._table[index]; while (trace != nullptr && trace->id() != id) { diff --git a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp index 7225dd601829..23403441a203 100644 --- a/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp +++ b/src/hotspot/share/jfr/support/jfrDeprecationEventWriter.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ void JfrDeprecatedEventWriterState::on_level_setting_update(int64_t new_level) { _current_level_setting = new_level; } -static inline bool level() { +static inline bool previous_epoch_level() { assert(_current_level_setting != uninitialized, "invariant"); return _previous_level_setting == uninitialized ? _current_level_setting : _previous_level_setting; } @@ -56,7 +56,7 @@ static inline bool level() { static inline bool only_for_removal() { assert(JfrEventSetting::is_enabled(JfrDeprecatedInvocationEvent), "invariant"); // level 0: forRemoval, level 1: = all - return level() == 0; + return previous_epoch_level() == 0; } void JfrDeprecatedStackTraceWriter::install_stacktrace_blob(JfrDeprecatedEdge* edge, JfrCheckpointWriter& writer, JavaThread* jt) { diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 39b0eb3656ca..3b170c0ea53f 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -225,7 +225,7 @@ void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) { assert(Thread::current() == t, "invariant"); assert(!tl->is_dead(), "invariant"); assert(tl->shelved_buffer() == nullptr, "invariant"); - tl->_dead = true; + AtomicAccess::store(&tl->_dead, true); tl->release(t); } diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index fc3b06d979a3..f6b85cae6ac1 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -341,7 +341,7 @@ class JfrThreadLocal { } bool is_dead() const { - return _dead; + return AtomicAccess::load(&_dead); } bool in_sampling_critical_section() const { diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp index ced01cfaa48a..803493f4a067 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp @@ -113,7 +113,7 @@ void JfrFilterClassClosure::iterate_all_classes(GrowableArrayis_nonempty()) { for (int i = 0; i < instrumented_klasses->length(); ++i) { - if (JfrKlassUnloading::is_unloaded(instrumented_klasses->at(i).trace_id())) { + if (JfrKlassUnloading::is_unloaded(instrumented_klasses->at(i).trace_id(), true)) { continue; } add(instrumented_klasses->at(i).instance_klass()); diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index 3e828d51ec36..64cb01878bd9 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -119,6 +119,7 @@ class JfrSetStorage : public AnyObj { ::new (&_table[i]) K{}; } } + _elements = 0; } }; diff --git a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp index 3a70d416058e..09db3a284940 100644 --- a/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp +++ b/src/hotspot/share/jfr/writers/jfrJavaEventWriter.cpp @@ -101,7 +101,7 @@ static bool setup_event_writer_offsets(TRAPS) { assert(valid_offset != invalid_offset, "invariant"); const char pin_name[] = "pinVirtualThread"; - Symbol* const pin_sym = SymbolTable::new_symbol(valid_name); + Symbol* const pin_sym = SymbolTable::new_symbol(pin_name); assert(pin_sym != nullptr, "invariant"); assert(invalid_offset == pin_offset, "invariant"); JfrJavaSupport::compute_field_offset(pin_offset, klass, pin_sym, vmSymbols::bool_signature()); diff --git a/src/hotspot/share/logging/log.hpp b/src/hotspot/share/logging/log.hpp index e2541f34662d..3e783d05b36c 100644 --- a/src/hotspot/share/logging/log.hpp +++ b/src/hotspot/share/logging/log.hpp @@ -111,7 +111,7 @@ class LogImpl { // Make sure no more than the maximum number of tags have been given. // The GuardTag allows this to be detected if/when it happens. If the GuardTag // is not __NO_TAG, the number of tags given exceeds the maximum allowed. - STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported! + static_assert(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported! // Empty constructor to avoid warnings on MSVC about unused variables // when the log instance is only used for static functions. diff --git a/src/hotspot/share/logging/logDecorators.hpp b/src/hotspot/share/logging/logDecorators.hpp index fff145729e47..33a268d98431 100644 --- a/src/hotspot/share/logging/logDecorators.hpp +++ b/src/hotspot/share/logging/logDecorators.hpp @@ -86,7 +86,7 @@ class LogDecorators { template static DefaultUndecoratedSelection make() { - STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); + static_assert(GuardTag == LogTag::__NO_TAG); return DefaultUndecoratedSelection(Level, T0, T1, T2, T3, T4); } diff --git a/src/hotspot/share/logging/logPrefix.hpp b/src/hotspot/share/logging/logPrefix.hpp index 49f7b7991c3a..6da64d77d7ef 100644 --- a/src/hotspot/share/logging/logPrefix.hpp +++ b/src/hotspot/share/logging/logPrefix.hpp @@ -100,7 +100,7 @@ DEBUG_ONLY(size_t Test_log_prefix_prefixer(char* buf, size_t len);) // The empty prefix, used when there's no prefix defined. template struct LogPrefix : public AllStatic { - STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); + static_assert(GuardTag == LogTag::__NO_TAG); static size_t prefix(char* buf, size_t len) { return 0; } diff --git a/src/hotspot/share/logging/logTagSet.hpp b/src/hotspot/share/logging/logTagSet.hpp index f8b37e1a777c..a4cdec987c60 100644 --- a/src/hotspot/share/logging/logTagSet.hpp +++ b/src/hotspot/share/logging/logTagSet.hpp @@ -154,7 +154,7 @@ template ; } else { diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 43bd5e452c8d..e5f2026e4589 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -563,7 +563,7 @@ void Metaspace::print_compressed_class_space(outputStream* st) { // Given a prereserved space, use that to set up the compressed class space list. void Metaspace::initialize_class_space(ReservedSpace rs) { - STATIC_ASSERT(INCLUDE_CLASS_SPACE == 1); + static_assert(INCLUDE_CLASS_SPACE == 1); assert(rs.size() >= CompressedClassSpaceSize, "%zu != %zu", rs.size(), CompressedClassSpaceSize); diff --git a/src/hotspot/share/memory/metaspace/binList.hpp b/src/hotspot/share/memory/metaspace/binList.hpp index 18e1b2c7fb86..c3bd3b1d843c 100644 --- a/src/hotspot/share/memory/metaspace/binList.hpp +++ b/src/hotspot/share/memory/metaspace/binList.hpp @@ -82,8 +82,8 @@ class BinListImpl { #define BLOCK_FORMAT_ARGS(b, sz) p2i(b), (sz), p2i((b)->_next) // Block size must be exactly one word size. - STATIC_ASSERT(sizeof(Block) == BytesPerWord); - STATIC_ASSERT(num_lists > 0); + static_assert(sizeof(Block) == BytesPerWord); + static_assert(num_lists > 0); public: diff --git a/src/hotspot/share/memory/metaspace/chunklevel.hpp b/src/hotspot/share/memory/metaspace/chunklevel.hpp index 559ff1fa46ce..bcef91dccf48 100644 --- a/src/hotspot/share/memory/metaspace/chunklevel.hpp +++ b/src/hotspot/share/memory/metaspace/chunklevel.hpp @@ -111,9 +111,9 @@ static const chunklevel_t CHUNK_LEVEL_4K = (ROOT_CHUNK_LEVEL + 12); static const chunklevel_t CHUNK_LEVEL_2K = (ROOT_CHUNK_LEVEL + 13); static const chunklevel_t CHUNK_LEVEL_1K = (ROOT_CHUNK_LEVEL + 14); -STATIC_ASSERT(CHUNK_LEVEL_1K == HIGHEST_CHUNK_LEVEL); -STATIC_ASSERT(CHUNK_LEVEL_16M == LOWEST_CHUNK_LEVEL); -STATIC_ASSERT(ROOT_CHUNK_LEVEL == LOWEST_CHUNK_LEVEL); +static_assert(CHUNK_LEVEL_1K == HIGHEST_CHUNK_LEVEL); +static_assert(CHUNK_LEVEL_16M == LOWEST_CHUNK_LEVEL); +static_assert(ROOT_CHUNK_LEVEL == LOWEST_CHUNK_LEVEL); ///////////////////////////////////////////////////////// // print helpers diff --git a/src/hotspot/share/memory/metaspace/counters.hpp b/src/hotspot/share/memory/metaspace/counters.hpp index 70c3e3413297..66b1ab7f40d3 100644 --- a/src/hotspot/share/memory/metaspace/counters.hpp +++ b/src/hotspot/share/memory/metaspace/counters.hpp @@ -43,7 +43,7 @@ class AbstractCounter { T _c; // Only allow unsigned values for now - STATIC_ASSERT(std::is_signed::value == false); + static_assert(std::is_signed::value == false); public: @@ -86,7 +86,7 @@ class AbstractAtomicCounter { volatile T _c; // Only allow unsigned values for now - STATIC_ASSERT(std::is_signed::value == false); + static_assert(std::is_signed::value == false); public: diff --git a/src/hotspot/share/memory/metaspace/freeBlocks.hpp b/src/hotspot/share/memory/metaspace/freeBlocks.hpp index 77ab48e17ea1..9b5dda158167 100644 --- a/src/hotspot/share/memory/metaspace/freeBlocks.hpp +++ b/src/hotspot/share/memory/metaspace/freeBlocks.hpp @@ -71,7 +71,7 @@ class FreeBlocks : public CHeapObj { // This verifies that blocks too large to go into the binlist can be // kept in the blocktree. - STATIC_ASSERT(BinList32::MaxWordSize >= BlockTree::MinWordSize); + static_assert(BinList32::MaxWordSize >= BlockTree::MinWordSize); public: diff --git a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp index e9f51d05aa5c..11ca416baa04 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceCommon.hpp @@ -53,7 +53,7 @@ static const size_t AllocationAlignmentWordSize = AllocationAlignmentByteSize / // Returns the raw word size allocated for a given net allocation. This only matters on 32-bit, where // allocations have to be 64-bit aligned too and therefore must be 2-word-aligned. inline size_t get_raw_word_size_for_requested_word_size(size_t word_size) { - LP64_ONLY(STATIC_ASSERT(AllocationAlignmentWordSize == 1)); // rewrite if this does not hold true anymore + LP64_ONLY(static_assert(AllocationAlignmentWordSize == 1)); // rewrite if this does not hold true anymore return LP64_ONLY(word_size) // no-op on 64-bit NOT_LP64(align_up(word_size, AllocationAlignmentWordSize)); } diff --git a/src/hotspot/share/memory/padded.inline.hpp b/src/hotspot/share/memory/padded.inline.hpp index ba477bfe88f0..0618d6fc602b 100644 --- a/src/hotspot/share/memory/padded.inline.hpp +++ b/src/hotspot/share/memory/padded.inline.hpp @@ -37,7 +37,7 @@ template PaddedEnd* PaddedArray::create_unfreeable(uint length) { // Check that the PaddedEnd class works as intended. - STATIC_ASSERT(is_aligned(sizeof(PaddedEnd), alignment)); + static_assert(is_aligned(sizeof(PaddedEnd), alignment)); // Allocate a chunk of memory large enough to allow for some alignment. void* chunk = AllocateHeap(length * sizeof(PaddedEnd) + alignment, MT); diff --git a/src/hotspot/share/nmt/arrayWithFreeList.hpp b/src/hotspot/share/nmt/arrayWithFreeList.hpp index 3d38013f3184..457ff5915b9d 100644 --- a/src/hotspot/share/nmt/arrayWithFreeList.hpp +++ b/src/hotspot/share/nmt/arrayWithFreeList.hpp @@ -37,7 +37,7 @@ class ArrayWithFreeList { // An E must be trivially copyable and destructible, but it may be constructed // however it likes. constexpr void static_assert_E_satisfies_type_requirements() const { - static_assert(std::is_trivially_copyable::value && std::is_trivially_destructible::value, "must be"); + static_assert(std::is_trivially_copyable::value && std::is_trivially_destructible::value); } public: diff --git a/src/hotspot/share/nmt/mallocHeader.cpp b/src/hotspot/share/nmt/mallocHeader.cpp index e74e74fde48e..f2fb213ab6ec 100644 --- a/src/hotspot/share/nmt/mallocHeader.cpp +++ b/src/hotspot/share/nmt/mallocHeader.cpp @@ -35,7 +35,7 @@ // The malloc header, as well as the coming VMATree implementation, rely on MemTag // fitting into eight bits. -STATIC_ASSERT(sizeof(MemTag) == sizeof(uint8_t)); +static_assert(sizeof(MemTag) == sizeof(uint8_t)); void MallocHeader::print_block_on_error(outputStream* st, address bad_address, address block_address) const { assert(bad_address >= (address)this, "sanity"); diff --git a/src/hotspot/share/nmt/mallocHeader.hpp b/src/hotspot/share/nmt/mallocHeader.hpp index 33e288dd8e42..0c4330ffc543 100644 --- a/src/hotspot/share/nmt/mallocHeader.hpp +++ b/src/hotspot/share/nmt/mallocHeader.hpp @@ -172,6 +172,6 @@ class MallocHeader { }; // This needs to be true on both 64-bit and 32-bit platforms -STATIC_ASSERT(sizeof(MallocHeader) == (sizeof(uint64_t) * 2)); +static_assert(sizeof(MallocHeader) == (sizeof(uint64_t) * 2)); #endif // SHARE_NMT_MALLOCHEADER_HPP \ No newline at end of file diff --git a/src/hotspot/share/nmt/mallocSiteTable.cpp b/src/hotspot/share/nmt/mallocSiteTable.cpp index d0b4bbe379fa..8c54786621c7 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.cpp +++ b/src/hotspot/share/nmt/mallocSiteTable.cpp @@ -32,7 +32,7 @@ Atomic* MallocSiteTable::_table = nullptr; const NativeCallStack* MallocSiteTable::_hash_entry_allocation_stack = nullptr; const MallocSiteHashtableEntry* MallocSiteTable::_hash_entry_allocation_site = nullptr; -Atomic MallocSiteTable::_entry_count(0); +Atomic MallocSiteTable::_entry_count(0); /* * Initialize malloc site table. @@ -83,7 +83,7 @@ bool MallocSiteTable::initialize() { // Add the allocation site to hashtable. int index = hash_to_index(entry.hash()); _table[index].store_relaxed(const_cast(&entry)); - _entry_count.add_then_fetch(1ul, memory_order_relaxed); + _entry_count.add_then_fetch(1, memory_order_relaxed); return true; } @@ -129,7 +129,7 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* // swap in the head if (_table[index].compare_set(nullptr, entry)) { - _entry_count.add_then_fetch(1ul, memory_order_relaxed); + _entry_count.add_then_fetch(1, memory_order_relaxed); *marker = build_marker(index, 0); return entry->data(); } @@ -155,7 +155,7 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* if (head->atomic_insert(entry)) { pos_idx ++; *marker = build_marker(index, pos_idx); - _entry_count.add_then_fetch(1ul, memory_order_relaxed); + _entry_count.add_then_fetch(1, memory_order_relaxed); return entry->data(); } // contended, other thread won @@ -242,7 +242,7 @@ void MallocSiteTable::print_tuning_statistics(outputStream* st) { st->print_cr("Malloc allocation site table:"); #ifdef ASSERT // This is solely for testing - st->print_cr("\tExpected entry count: %zu", _entry_count.load_relaxed()); + st->print_cr("\tExpected entry count: %d", _entry_count.load_relaxed()); #endif st->print_cr("\tTotal entries: %d", total_entries); st->print_cr("\tEmpty entries (no outstanding mallocs): %d (%2.2f%%)", diff --git a/src/hotspot/share/nmt/mallocSiteTable.hpp b/src/hotspot/share/nmt/mallocSiteTable.hpp index 22b944301d39..49604a4e7ef5 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.hpp +++ b/src/hotspot/share/nmt/mallocSiteTable.hpp @@ -116,7 +116,7 @@ class MallocSiteTable : AllStatic { // Each bucket chain cannot be longer than what a 16 bit pos idx can hold (hopefully way shorter) #define MAX_BUCKET_LENGTH (USHRT_MAX - 1) - STATIC_ASSERT(table_size <= MAX_MALLOCSITE_TABLE_SIZE); + static_assert(table_size <= MAX_MALLOCSITE_TABLE_SIZE); static uint32_t build_marker(unsigned bucket_idx, unsigned pos_idx) { assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE && pos_idx < MAX_BUCKET_LENGTH, "overflow"); @@ -172,6 +172,10 @@ class MallocSiteTable : AllStatic { static void print_tuning_statistics(outputStream* st); + static int entry_count() { + return _entry_count.load_relaxed(); + } + private: static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key, MemTag mem_tag); @@ -196,7 +200,7 @@ class MallocSiteTable : AllStatic { private: // The callsite hashtable. It has to be a static table, // since malloc call can come from C runtime linker. - static Atomic _entry_count; + static Atomic _entry_count; static Atomic* _table; static const NativeCallStack* _hash_entry_allocation_stack; static const MallocSiteHashtableEntry* _hash_entry_allocation_site; diff --git a/src/hotspot/share/nmt/memBaseline.cpp b/src/hotspot/share/nmt/memBaseline.cpp index 0e66e686f544..d5d8574934dd 100644 --- a/src/hotspot/share/nmt/memBaseline.cpp +++ b/src/hotspot/share/nmt/memBaseline.cpp @@ -74,32 +74,54 @@ int compare_virtual_memory_site(const VirtualMemoryAllocationSite& s1, return s1.call_stack()->compare(*s2.call_stack()); } -/* - * Walker to walk malloc allocation site table - */ -class MallocAllocationSiteWalker : public MallocSiteWalker { - private: - SortedLinkedList _malloc_sites; - // Entries in MallocSiteTable with size = 0 and count = 0, - // when the malloc site is not longer there. - public: +class MallocAllocationSiteWalker : public MallocSiteWalker { + MallocSite* _malloc_sites; + int _length; + int _index; + +public: + MallocAllocationSiteWalker(int entry_count) + : MallocSiteWalker(), _malloc_sites(NEW_C_HEAP_ARRAY_RETURN_NULL(MallocSite, entry_count, mtNMT)), + _length(_malloc_sites == nullptr ? 0 : entry_count), _index(0) { + } - LinkedList* malloc_sites() { - return &_malloc_sites; + MallocSite* malloc_sites(int* len) { + *len = _index; + MallocSite* r = REALLOC_C_HEAP_ARRAY_RETURN_NULL(_malloc_sites, _index, mtNMT); + if (r == nullptr) { + return _malloc_sites; + } + return r; } - bool do_malloc_site(const MallocSite* site) { + bool do_malloc_site(const MallocSite* site) override { + assert(_malloc_sites != nullptr, "must be"); + // Ignore empty sites. if (site->size() > 0) { - if (_malloc_sites.add(*site) != nullptr) { - return true; - } else { - return false; // OOM + // There may be inserts concurrent with this traversal + // so we must check whether we still have room for another site. + if (_index >= _length) { + // Ran out of space, we'll do a conservative resize. + // Casting float to int is floor(), and we want to increase count by at least 1. + int new_len = int(float(_length) * 1.2) + 1; + MallocSite* r = REALLOC_C_HEAP_ARRAY_RETURN_NULL(_malloc_sites, new_len, mtNMT); + if (r == nullptr) { + // Failed, we can bail and let the report continue with an incomplete one. + return false; + } + _malloc_sites = r; + _length = new_len; } - } else { - // Ignore empty sites. - return true; + new (&_malloc_sites[_index]) MallocSite(*site); + _index++; } + return true; + } + + + bool allocation_failed() { + return _malloc_sites == nullptr; } }; @@ -115,18 +137,16 @@ void MemBaseline::baseline_summary() { } bool MemBaseline::baseline_allocation_sites() { - // Malloc allocation sites - MallocAllocationSiteWalker malloc_walker; - if (!MallocSiteTable::walk_malloc_site(&malloc_walker)) { + // Malloc allocation sites. + MallocAllocationSiteWalker malloc_walker(MallocSiteTable::entry_count()); + if (malloc_walker.allocation_failed()) { return false; } - - _malloc_sites.move(malloc_walker.malloc_sites()); - // The malloc sites are collected in size order - _malloc_sites_order = by_size; + MallocSiteTable::walk_malloc_site(&malloc_walker); + _malloc_sites = malloc_walker.malloc_sites(&_malloc_sites_length); + sort_malloc_sites(by_size); assert(_vma_allocations == nullptr, "must"); - { MemTracker::NmtVirtualMemoryLocker locker; _vma_allocations = new (mtNMT, std::nothrow) RegionsTree(*VirtualMemoryTracker::Instance::tree()); @@ -195,92 +215,50 @@ bool MemBaseline::aggregate_virtual_memory_allocation_sites() { return true; } -MallocSiteIterator MemBaseline::malloc_sites(SortingOrder order) { - assert(!_malloc_sites.is_empty(), "Not detail baseline"); - switch(order) { - case by_size: - malloc_sites_to_size_order(); - break; - case by_site: - malloc_sites_to_allocation_site_order(); - break; - case by_site_and_tag: - malloc_sites_to_allocation_site_and_tag_order(); - break; - default: - ShouldNotReachHere(); - } - return MallocSiteIterator(_malloc_sites.head()); -} - -void MemBaseline::sort_virtual_memory_sites(SortingOrder order) { - assert(_virtual_memory_sites_length != 0, "Not detail baseline"); - switch(order) { - case by_size: - virtual_memory_sites_to_size_order(); - break; - case by_site: - virtual_memory_sites_to_reservation_site_order(); - break; - default: - ShouldNotReachHere(); - } -} - - -// Sorting allocations sites in different orders -void MemBaseline::malloc_sites_to_size_order() { - if (_malloc_sites_order != by_size) { - SortedLinkedList tmp; - - // Add malloc sites to sorted linked list to sort into size order - tmp.move(&_malloc_sites); - _malloc_sites.set_head(tmp.head()); - tmp.set_head(nullptr); - _malloc_sites_order = by_size; - } -} - -void MemBaseline::malloc_sites_to_allocation_site_order() { - if (_malloc_sites_order != by_site && _malloc_sites_order != by_site_and_tag) { - SortedLinkedList tmp; - // Add malloc sites to sorted linked list to sort into site (address) order - tmp.move(&_malloc_sites); - _malloc_sites.set_head(tmp.head()); - tmp.set_head(nullptr); - _malloc_sites_order = by_site; - } -} - -void MemBaseline::malloc_sites_to_allocation_site_and_tag_order() { - if (_malloc_sites_order != by_site_and_tag) { - SortedLinkedList tmp; - // Add malloc sites to sorted linked list to sort into site (address) order - tmp.move(&_malloc_sites); - _malloc_sites.set_head(tmp.head()); - tmp.set_head(nullptr); - _malloc_sites_order = by_site_and_tag; - } -} - -void MemBaseline::virtual_memory_sites_to_size_order() { - if (_virtual_memory_sites_order != by_size) { - ::qsort(_virtual_memory_sites, _virtual_memory_sites_length, sizeof(VirtualMemoryAllocationSite), +template +static void qsort_helper(T* array, int length) { + ::qsort(array, length, sizeof(T), [](const void* a, const void* b) { - return compare_virtual_memory_size(*static_cast(a), - *static_cast(b)); + return Cmp(*static_cast(a), + *static_cast(b)); }); - _virtual_memory_sites_order = by_size; +} +template +using Qsorter = void (*)(T*, int); + + +void MemBaseline::sort_malloc_sites(SortingOrder order) { + assert(_malloc_sites_length != 0, "Not detail baseline"); + assert(order != invalid, "must be"); + Qsorter sorter_by_sortingorder[SortingOrder::_count] = { + nullptr, + &qsort_helper, + &qsort_helper, + &qsort_helper + }; + + auto sort = sorter_by_sortingorder[order]; + guarantee(sort != nullptr, "An invalid sorting method was selected"); + if (_malloc_sites_order != order) { + sort(_malloc_sites, _malloc_sites_length); + _malloc_sites_order = order; } } -void MemBaseline::virtual_memory_sites_to_reservation_site_order() { - if (_virtual_memory_sites_order != by_site) { - ::qsort(_virtual_memory_sites, _virtual_memory_sites_length, sizeof(VirtualMemoryAllocationSite), - [](const void* a, const void* b) { - return compare_virtual_memory_site(*static_cast(a), - *static_cast(b)); - }); - _virtual_memory_sites_order = by_site; +void MemBaseline::sort_virtual_memory_sites(SortingOrder order) { + assert(_virtual_memory_sites_length != 0, "Not detail baseline"); + assert(order != invalid, "must be"); + Qsorter sorter_by_sortingorder[SortingOrder::_count] = { + nullptr, + &qsort_helper, + &qsort_helper, + nullptr + }; + + auto sort = sorter_by_sortingorder[order]; + guarantee(sort != nullptr, "An invalid sorting method was selected"); + if (_virtual_memory_sites_order != order) { + sort(_virtual_memory_sites, _virtual_memory_sites_length); + _virtual_memory_sites_order = order; } } diff --git a/src/hotspot/share/nmt/memBaseline.hpp b/src/hotspot/share/nmt/memBaseline.hpp index f0e02a202dd2..1b36120a506e 100644 --- a/src/hotspot/share/nmt/memBaseline.hpp +++ b/src/hotspot/share/nmt/memBaseline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,9 +32,6 @@ #include "nmt/nmtHashTable.hpp" #include "nmt/virtualMemoryTracker.hpp" #include "runtime/mutex.hpp" -#include "utilities/linkedlist.hpp" - -typedef LinkedListIterator MallocSiteIterator; /* * Baseline a memory snapshot @@ -49,10 +46,12 @@ class MemBaseline { }; enum SortingOrder { + by_address, // by memory address by_size, // by memory size by_site, // by call site where the memory is allocated from by_site_and_tag, // by call site and memory tag - invalid + _count, + invalid = _count }; private: @@ -67,7 +66,8 @@ class MemBaseline { // Allocation sites information // Malloc allocation sites - LinkedListImpl _malloc_sites; + MallocSite* _malloc_sites; + int _malloc_sites_length; // All virtual memory allocations RegionsTree* _vma_allocations; @@ -86,6 +86,7 @@ class MemBaseline { // create a memory baseline MemBaseline(): _instance_class_count(0), _array_class_count(0), _thread_count(0), + _malloc_sites(nullptr), _malloc_sites_length(0), _vma_allocations(nullptr), _virtual_memory_sites(nullptr), _virtual_memory_sites_length(0), _malloc_sites_order(invalid), @@ -113,9 +114,12 @@ class MemBaseline { return _metaspace_stats; } - MallocSiteIterator malloc_sites(SortingOrder order); + void sort_malloc_sites(SortingOrder order); void sort_virtual_memory_sites(SortingOrder order); + MallocSite* malloc_sites() { return _malloc_sites; } + int malloc_sites_length() { return _malloc_sites_length; } + VirtualMemoryAllocationSite* virtual_memory_sites() { return _virtual_memory_sites; } int virtual_memory_sites_length() { return _virtual_memory_sites_length; } @@ -194,7 +198,10 @@ class MemBaseline { _array_class_count = 0; _thread_count = 0; - _malloc_sites.clear(); + os::free(_malloc_sites); + _malloc_sites_length = 0; + _malloc_sites = nullptr; + _malloc_sites_order = invalid; os::free(_virtual_memory_sites); _virtual_memory_sites_length = 0; _virtual_memory_sites = nullptr; @@ -212,19 +219,6 @@ class MemBaseline { // Aggregate virtual memory allocation by allocation sites bool aggregate_virtual_memory_allocation_sites(); - - // Sorting allocation sites in different orders - // Sort allocation sites in size order - void malloc_sites_to_size_order(); - // Sort allocation sites in call site address order - void malloc_sites_to_allocation_site_order(); - // Sort allocation sites in call site address and memory tag order - void malloc_sites_to_allocation_site_and_tag_order(); - - // Sort allocation sites in reserved size order - void virtual_memory_sites_to_size_order(); - // Sort allocation sites in call site address order - void virtual_memory_sites_to_reservation_site_order(); }; #endif // SHARE_NMT_MEMBASELINE_HPP diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index c226b4d3078f..fce29fac8bea 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -323,14 +323,14 @@ void MemDetailReporter::report_detail() { } int MemDetailReporter::report_malloc_sites() { - MallocSiteIterator malloc_itr = _baseline.malloc_sites(MemBaseline::by_size); - if (malloc_itr.is_empty()) return 0; + _baseline.sort_malloc_sites(MemBaseline::by_size); outputStream* out = output(); - const MallocSite* malloc_site; + const MallocSite* malloc_site = nullptr; int num_omitted = 0; - while ((malloc_site = malloc_itr.next()) != nullptr) { + for (int i = 0; i < _baseline.malloc_sites_length(); i++) { + malloc_site = &_baseline.malloc_sites()[i]; // Omit printing if the current value and the historic peak value both fall below the reporting scale threshold if (amount_in_current_scale(MAX2(malloc_site->size(), malloc_site->peak_size())) == 0) { num_omitted ++; @@ -340,11 +340,11 @@ int MemDetailReporter::report_malloc_sites() { _stackprinter.print_stack(stack); MemTag mem_tag = malloc_site->mem_tag(); assert(NMTUtil::tag_is_valid(mem_tag) && mem_tag != mtNone, - "Must have a valid memory tag"); + "Must have a valid memory tag"); INDENT_BY(29, - out->print("("); - print_malloc(malloc_site->counter(), mem_tag); - out->print_cr(")"); + out->print("("); + print_malloc(malloc_site->counter(), mem_tag); + out->print_cr(")"); ) out->cr(); } @@ -801,31 +801,55 @@ void MemDetailDiffReporter::report_diff() { } void MemDetailDiffReporter::diff_malloc_sites() const { - MallocSiteIterator early_itr = _early_baseline.malloc_sites(MemBaseline::by_site_and_tag); - MallocSiteIterator current_itr = _current_baseline.malloc_sites(MemBaseline::by_site_and_tag); + _early_baseline.sort_malloc_sites(MemBaseline::by_site_and_tag); + _current_baseline.sort_malloc_sites(MemBaseline::by_site_and_tag); + + const MallocSite* early_site = nullptr; + const MallocSite* current_site = nullptr; + int early_i = 0; + int current_i = 0; + auto early_next = [&]() -> const MallocSite* { + if (early_i < _early_baseline.malloc_sites_length()) { + const MallocSite* e = &_early_baseline.malloc_sites()[early_i]; + early_i++; + return e; + } else { + return nullptr; + } + }; + auto current_next = [&]() -> const MallocSite* { + if (current_i < _current_baseline.malloc_sites_length()) { + const MallocSite* e = &_current_baseline.malloc_sites()[current_i]; + current_i++; + return e; + } else { + return nullptr; + } + }; + - const MallocSite* early_site = early_itr.next(); - const MallocSite* current_site = current_itr.next(); + early_site = early_next(); + current_site = current_next(); while (early_site != nullptr || current_site != nullptr) { if (early_site == nullptr) { new_malloc_site(current_site); - current_site = current_itr.next(); + current_site = current_next(); } else if (current_site == nullptr) { old_malloc_site(early_site); - early_site = early_itr.next(); + early_site = early_next(); } else { int compVal = current_site->call_stack()->compare(*early_site->call_stack()); if (compVal < 0) { new_malloc_site(current_site); - current_site = current_itr.next(); + current_site = current_next(); } else if (compVal > 0) { old_malloc_site(early_site); - early_site = early_itr.next(); + early_site = early_next(); } else { diff_malloc_site(early_site, current_site); - early_site = early_itr.next(); - current_site = current_itr.next(); + early_site = early_next(); + current_site = current_next(); } } } diff --git a/src/hotspot/share/nmt/memTracker.cpp b/src/hotspot/share/nmt/memTracker.cpp index a07c8bd69ef7..3b487147cd5d 100644 --- a/src/hotspot/share/nmt/memTracker.cpp +++ b/src/hotspot/share/nmt/memTracker.cpp @@ -65,7 +65,7 @@ void MemTracker::initialize() { // Memory tag is encoded into tracking header as a byte field, // make sure that we don't overflow it. - STATIC_ASSERT(mt_number_of_tags <= max_jubyte); + static_assert(mt_number_of_tags <= max_jubyte); if (level > NMT_off) { _baseline.initialize(); diff --git a/src/hotspot/share/nmt/nmtCommon.cpp b/src/hotspot/share/nmt/nmtCommon.cpp index 834f27152ee3..0d3b5e5fdd29 100644 --- a/src/hotspot/share/nmt/nmtCommon.cpp +++ b/src/hotspot/share/nmt/nmtCommon.cpp @@ -25,9 +25,9 @@ #include "nmt/nmtCommon.hpp" #include "utilities/globalDefinitions.hpp" -STATIC_ASSERT(NMT_off > NMT_unknown); -STATIC_ASSERT(NMT_summary > NMT_off); -STATIC_ASSERT(NMT_detail > NMT_summary); +static_assert(NMT_off > NMT_unknown); +static_assert(NMT_summary > NMT_off); +static_assert(NMT_detail > NMT_summary); #define MEMORY_TAG_DECLARE_NAME(tag, human_readable) \ { #tag, human_readable }, diff --git a/src/hotspot/share/oops/access.hpp b/src/hotspot/share/oops/access.hpp index 2ab0ed556f53..330daf7ece41 100644 --- a/src/hotspot/share/oops/access.hpp +++ b/src/hotspot/share/oops/access.hpp @@ -363,22 +363,22 @@ class ArrayAccess: public HeapAccess { template template void Access::verify_decorators() { - STATIC_ASSERT((~expected_decorators & decorators) == 0); // unexpected decorator used + static_assert((~expected_decorators & decorators) == 0); // unexpected decorator used const DecoratorSet barrier_strength_decorators = decorators & AS_DECORATOR_MASK; - STATIC_ASSERT(barrier_strength_decorators == 0 || ( // make sure barrier strength decorators are disjoint if set + static_assert(barrier_strength_decorators == 0 || ( // make sure barrier strength decorators are disjoint if set (barrier_strength_decorators ^ AS_NO_KEEPALIVE) == 0 || (barrier_strength_decorators ^ AS_RAW) == 0 || (barrier_strength_decorators ^ AS_NORMAL) == 0 )); const DecoratorSet ref_strength_decorators = decorators & ON_DECORATOR_MASK; - STATIC_ASSERT(ref_strength_decorators == 0 || ( // make sure ref strength decorators are disjoint if set + static_assert(ref_strength_decorators == 0 || ( // make sure ref strength decorators are disjoint if set (ref_strength_decorators ^ ON_STRONG_OOP_REF) == 0 || (ref_strength_decorators ^ ON_WEAK_OOP_REF) == 0 || (ref_strength_decorators ^ ON_PHANTOM_OOP_REF) == 0 || (ref_strength_decorators ^ ON_UNKNOWN_OOP_REF) == 0 )); const DecoratorSet memory_ordering_decorators = decorators & MO_DECORATOR_MASK; - STATIC_ASSERT(memory_ordering_decorators == 0 || ( // make sure memory ordering decorators are disjoint if set + static_assert(memory_ordering_decorators == 0 || ( // make sure memory ordering decorators are disjoint if set (memory_ordering_decorators ^ MO_UNORDERED) == 0 || (memory_ordering_decorators ^ MO_RELAXED) == 0 || (memory_ordering_decorators ^ MO_ACQUIRE) == 0 || @@ -386,7 +386,7 @@ void Access::verify_decorators() { (memory_ordering_decorators ^ MO_SEQ_CST) == 0 )); const DecoratorSet location_decorators = decorators & IN_DECORATOR_MASK; - STATIC_ASSERT(location_decorators == 0 || ( // make sure location decorators are disjoint if set + static_assert(location_decorators == 0 || ( // make sure location decorators are disjoint if set (location_decorators ^ IN_NATIVE) == 0 || (location_decorators ^ IN_HEAP) == 0 )); diff --git a/src/hotspot/share/oops/accessBackend.hpp b/src/hotspot/share/oops/accessBackend.hpp index 531530c13bae..ebc7c5affc41 100644 --- a/src/hotspot/share/oops/accessBackend.hpp +++ b/src/hotspot/share/oops/accessBackend.hpp @@ -1136,9 +1136,9 @@ namespace AccessInternal { inline void verify_types(){ // If this fails to compile, then you have sent in something that is // not recognized as a valid primitive type to a primitive Access function. - STATIC_ASSERT((HasDecorator::value || // oops have already been validated - (std::is_pointer::value || std::is_integral::value) || - std::is_floating_point::value)); // not allowed primitive type + static_assert(HasDecorator::value || // oops have already been validated + std::is_pointer::value || std::is_integral::value || + std::is_floating_point::value); // not allowed primitive type } template @@ -1256,9 +1256,9 @@ namespace AccessInternal { inline OopCopyResult arraycopy(arrayOop src_obj, size_t src_offset_in_bytes, const T* src_raw, arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw, size_t length) { - STATIC_ASSERT((HasDecorator::value || - (std::is_same::value || std::is_integral::value) || - std::is_floating_point::value)); // arraycopy allows type erased void elements + static_assert(HasDecorator::value || + std::is_same::value || std::is_integral::value || + std::is_floating_point::value); // arraycopy allows type erased void elements using DecayedT = std::decay_t; const DecoratorSet expanded_decorators = DecoratorFixup::value; return arraycopy_reduce_types(src_obj, src_offset_in_bytes, const_cast(src_raw), diff --git a/src/hotspot/share/oops/annotations.cpp b/src/hotspot/share/oops/annotations.cpp index c4e49d523838..be420f062a68 100644 --- a/src/hotspot/share/oops/annotations.cpp +++ b/src/hotspot/share/oops/annotations.cpp @@ -87,13 +87,11 @@ void Annotations::print_value_on(outputStream* st) const { st->print("Annotations(" PTR_FORMAT ")", p2i(this)); } -#define BULLET " - " - #ifndef PRODUCT void Annotations::print_on(outputStream* st) const { - st->print(BULLET"class_annotations "); class_annotations()->print_value_on(st); - st->print(BULLET"fields_annotations "); fields_annotations()->print_value_on(st); - st->print(BULLET"class_type_annotations "); class_type_annotations()->print_value_on(st); - st->print(BULLET"fields_type_annotations "); fields_type_annotations()->print_value_on(st); + st->print(" - class_annotations "); class_annotations()->print_value_on(st); + st->print(" - fields_annotations "); fields_annotations()->print_value_on(st); + st->print(" - class_type_annotations "); class_type_annotations()->print_value_on(st); + st->print(" - fields_type_annotations "); fields_type_annotations()->print_value_on(st); } #endif // PRODUCT diff --git a/src/hotspot/share/oops/compressedOops.inline.hpp b/src/hotspot/share/oops/compressedOops.inline.hpp index 191c376686cf..bd52fb3177f4 100644 --- a/src/hotspot/share/oops/compressedOops.inline.hpp +++ b/src/hotspot/share/oops/compressedOops.inline.hpp @@ -111,7 +111,7 @@ inline uint32_t CompressedOops::narrow_oop_value(narrowOop o) { template inline narrowOop CompressedOops::narrow_oop_cast(T i) { - static_assert(std::is_integral::value, "precondition"); + static_assert(std::is_integral::value); uint32_t narrow_value = static_cast(i); // Ensure no bits lost in conversion to uint32_t. assert(i == static_cast(narrow_value), "narrowOop overflow"); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 4bba6c5ced7d..e96b79014a56 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -525,7 +525,7 @@ void ConstantPool::remove_unshareable_entries() { tag_at_put(cp_index, JVM_CONSTANT_Dynamic); break; case JVM_CONSTANT_Class: - remove_resolved_klass_if_non_deterministic(cp_index); + remove_resolved_klass_if_non_archivable(cp_index); break; default: break; @@ -538,7 +538,7 @@ void ConstantPool::remove_unshareable_entries() { } } -void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { +void ConstantPool::remove_resolved_klass_if_non_archivable(int cp_index) { assert(ArchiveBuilder::current()->is_in_buffer_space(this), "must be"); assert(tag_at(cp_index).is_klass(), "must be resolved"); diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 824ec6409eba..b87d52ea1f99 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -608,7 +608,7 @@ class ConstantPool : public Metadata { void restore_unshareable_info(TRAPS); private: void remove_unshareable_entries(); - void remove_resolved_klass_if_non_deterministic(int cp_index); + void remove_resolved_klass_if_non_archivable(int cp_index); template void iterate_archivable_resolved_references(Function function); #endif diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 5edc9813c9bd..73879b7656c0 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -401,15 +401,9 @@ void ConstantPoolCache::record_gc_epoch() { void ConstantPoolCache::remove_unshareable_info() { assert(CDSConfig::is_dumping_archive(), "sanity"); - if (_resolved_indy_entries != nullptr) { - remove_resolved_indy_entries_if_non_deterministic(); - } - if (_resolved_field_entries != nullptr) { - remove_resolved_field_entries_if_non_deterministic(); - } - if (_resolved_method_entries != nullptr) { - remove_resolved_method_entries_if_non_deterministic(); - } + remove_resolved_indy_entries_if_non_archivable(); + remove_resolved_field_entries_if_non_archivable(); + remove_resolved_method_entries_if_non_archivable(); #if INCLUDE_CDS_JAVA_HEAP _archived_references_index = -1; @@ -423,13 +417,16 @@ void ConstantPoolCache::remove_unshareable_info() { #endif } -void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { +template +void ConstantPoolCache::iterate_resolved_field_entries_with_archivability_check(FUNC f) { + if (_resolved_field_entries == nullptr) { + return; + } ConstantPool* cp = constant_pool(); - ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(cp); + ConstantPool* src_cp = ArchiveBuilder::current()->is_in_buffer_space(cp) ? ArchiveBuilder::current()->get_source_addr(cp) : cp; for (int i = 0; i < _resolved_field_entries->length(); i++) { ResolvedFieldEntry* rfi = _resolved_field_entries->adr_at(i); int cp_index = rfi->constant_pool_index(); - bool archived = false; bool resolved = false; if (rfi->is_resolved(Bytecodes::_getfield) || rfi->is_resolved(Bytecodes::_putfield) || @@ -437,10 +434,19 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { resolved = true; } - if (resolved && !CDSConfig::is_dumping_preimage_static_archive() - && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { + bool archivable = (resolved && !CDSConfig::is_dumping_preimage_static_archive() && + AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)); + f(cp, src_cp, cp_index, rfi, resolved, archivable); + } +} + +void ConstantPoolCache::remove_resolved_field_entries_if_non_archivable() { + ConstantPool* cp = constant_pool(); + iterate_resolved_field_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int cp_index, + ResolvedFieldEntry* rfi, bool resolved, + bool archivable) { + if (archivable) { rfi->mark_and_relocate(); - archived = true; } else { rfi->remove_unshareable_info(); } @@ -453,25 +459,28 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { Symbol* signature = cp->uncached_signature_ref_at(cp_index); if (resolved) { log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s%s", - (archived ? "archived" : "reverted"), + (archivable ? "archived" : "reverted"), cp_index, cp->pool_holder()->name()->as_C_string(), klass_name->as_C_string(), name->as_C_string(), signature->as_C_string(), rfi->is_resolved(Bytecodes::_getstatic) || rfi->is_resolved(Bytecodes::_putstatic) ? " *** static" : "", - (archived ? "" : " (resolution is not deterministic)")); + (archivable ? "" : " (resolution is not deterministic)")); } } - ArchiveBuilder::alloc_stats()->record_field_cp_entry(archived, resolved && !archived); - } + ArchiveBuilder::alloc_stats()->record_field_cp_entry(archivable, resolved && !archivable); + }); } -void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { +template +void ConstantPoolCache::iterate_resolved_method_entries_with_archivability_check(FUNC f) { + if (_resolved_method_entries == nullptr) { + return; + } ConstantPool* cp = constant_pool(); - ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(cp); + ConstantPool* src_cp = ArchiveBuilder::current()->is_in_buffer_space(cp) ? ArchiveBuilder::current()->get_source_addr(cp) : cp; for (int i = 0; i < _resolved_method_entries->length(); i++) { ResolvedMethodEntry* rme = _resolved_method_entries->adr_at(i); int cp_index = rme->constant_pool_index(); - bool archived = false; bool resolved = rme->is_resolved(Bytecodes::_invokevirtual) || rme->is_resolved(Bytecodes::_invokespecial) || rme->is_resolved(Bytecodes::_invokeinterface) || @@ -479,10 +488,18 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { (rme->is_resolved(Bytecodes::_invokestatic) && VM_Version::supports_fast_class_init_checks()); const char* rejection_reason = nullptr; - if (resolved && !CDSConfig::is_dumping_preimage_static_archive() - && can_archive_resolved_method(src_cp, rme, rejection_reason)) { + bool archivable = (resolved && !CDSConfig::is_dumping_preimage_static_archive() && + can_archive_resolved_method(src_cp, rme, rejection_reason)); + f(cp, src_cp, cp_index, rme, resolved, archivable, rejection_reason); + } +} + +void ConstantPoolCache::remove_resolved_method_entries_if_non_archivable() { + iterate_resolved_method_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int cp_index, + ResolvedMethodEntry* rme, bool resolved, + bool archivable, const char* rejection_reason) { + if (archivable) { rme->mark_and_relocate(src_cp); - archived = true; } else { rme->remove_unshareable_info(); } @@ -496,7 +513,7 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { LogStream ls(lt); if (resolved) { ls.print("%s%s method CP entry [%3d]: %s %s.%s:%s", - (archived ? "archived" : "reverted"), + (archivable ? "archived" : "reverted"), (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), cp_index, cp->pool_holder()->name()->as_C_string(), @@ -505,7 +522,7 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { ls.print(" %s", rejection_reason); } } - if (archived) { + if (archivable) { Klass* resolved_klass = cp->resolved_klass_at(klass_cp_index); ls.print(" => %s%s", resolved_klass->name()->as_C_string(), @@ -513,24 +530,35 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { } ls.cr(); } - ArchiveBuilder::alloc_stats()->record_method_cp_entry(archived, resolved && !archived); - } + ArchiveBuilder::alloc_stats()->record_method_cp_entry(archivable, resolved && !archivable); + }); } -void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { +template +void ConstantPoolCache::iterate_resolved_indy_entries_with_archivability_check(FUNC f) { + if (_resolved_indy_entries == nullptr) { + return; + } ConstantPool* cp = constant_pool(); - ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(cp); + ConstantPool* src_cp = ArchiveBuilder::current()->is_in_buffer_space(cp) ? ArchiveBuilder::current()->get_source_addr(cp) : cp; for (int i = 0; i < _resolved_indy_entries->length(); i++) { - ResolvedIndyEntry* rei = _resolved_indy_entries->adr_at(i); - int cp_index = rei->constant_pool_index(); - bool archived = false; - bool resolved = rei->is_resolved(); - if (resolved && !CDSConfig::is_dumping_preimage_static_archive() - && AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) { - rei->mark_and_relocate(); - archived = true; + ResolvedIndyEntry* rie = _resolved_indy_entries->adr_at(i); + int cp_index = rie->constant_pool_index(); + bool resolved = rie->is_resolved(); + bool archivable = (resolved && !CDSConfig::is_dumping_preimage_static_archive() && + AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)); + f(cp, src_cp, i, cp_index, rie, resolved, archivable); + } +} + +void ConstantPoolCache::remove_resolved_indy_entries_if_non_archivable() { + iterate_resolved_indy_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int i, int cp_index, + ResolvedIndyEntry* rie, bool resolved, + bool archivable) { + if (archivable) { + rie->mark_and_relocate(); } else { - rei->remove_unshareable_info(); + rie->remove_unshareable_info(); } LogStreamHandle(Trace, aot, resolve) log; if (log.is_enabled()) { @@ -542,15 +570,52 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); if (resolved) { log.print("%s indy CP entry [%3d]: %s (%d)", - (archived ? "archived" : "reverted"), + (archivable ? "archived" : "reverted"), cp_index, cp->pool_holder()->name()->as_C_string(), i); - log.print(" %s %s.%s:%s%s", (archived ? "=>" : " "), bsm_klass->as_C_string(), + log.print(" %s %s.%s:%s%s", (archivable ? "=>" : " "), bsm_klass->as_C_string(), bsm_name->as_C_string(), bsm_signature->as_C_string(), - (archived ? "" : " (resolution is not deterministic)")); + (archivable ? "" : " (resolution is not deterministic)")); } } - ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archived, resolved && !archived); - } + ArchiveBuilder::alloc_stats()->record_indy_cp_entry(archivable, resolved && !archivable); + }); +} + +// Called from AOTArtifactFinder. +void ConstantPoolCache::record_classes_in_archivable_entries() { + record_classes_in_archivable_field_entries(); + record_classes_in_archivable_indy_entries(); + record_classes_in_archivable_method_entries(); +} + +void ConstantPoolCache::record_classes_in_archivable_field_entries() { + iterate_resolved_field_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int cp_index, + ResolvedFieldEntry* rfi, bool resolved, + bool archivable) { + if (archivable) { + rfi->record_archivable_classes(); + } + }); +} + +void ConstantPoolCache::record_classes_in_archivable_indy_entries() { + iterate_resolved_indy_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int i, int cp_index, + ResolvedIndyEntry* rie, bool resolved, + bool archivable) { + if (archivable) { + rie->record_archivable_classes(); + } + }); +} + +void ConstantPoolCache::record_classes_in_archivable_method_entries() { + iterate_resolved_method_entries_with_archivability_check([&] (ConstantPool* cp, ConstantPool* src_cp, int cp_index, + ResolvedMethodEntry* rme, bool resolved, + bool archivable, const char* rejection_reason) { + if (archivable) { + rme->record_archivable_classes(); + } + }); } bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry, const char*& rejection_reason) { diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index 7f4532f99143..d55812e0a2d3 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -219,9 +219,20 @@ class ConstantPoolCache: public MetaspaceObj { #endif // INCLUDE_JVMTI #if INCLUDE_CDS - void remove_resolved_field_entries_if_non_deterministic(); - void remove_resolved_indy_entries_if_non_deterministic(); - void remove_resolved_method_entries_if_non_deterministic(); + private: + template void iterate_resolved_field_entries_with_archivability_check(FUNC f); + template void iterate_resolved_indy_entries_with_archivability_check(FUNC f); + template void iterate_resolved_method_entries_with_archivability_check(FUNC f); + + void record_classes_in_archivable_field_entries(); + void record_classes_in_archivable_indy_entries(); + void record_classes_in_archivable_method_entries(); + + void remove_resolved_field_entries_if_non_archivable(); + void remove_resolved_indy_entries_if_non_archivable(); + void remove_resolved_method_entries_if_non_archivable(); + public: + void record_classes_in_archivable_entries(); bool can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry, const char*& rejection_reason); #endif diff --git a/src/hotspot/share/oops/fieldInfo.cpp b/src/hotspot/share/oops/fieldInfo.cpp index c6392c1917af..083cad771886 100644 --- a/src/hotspot/share/oops/fieldInfo.cpp +++ b/src/hotspot/share/oops/fieldInfo.cpp @@ -337,7 +337,7 @@ int FieldInfoReader::search_table_lookup(const Array* search_table, const Sy FieldInfoComparator comp(this, cp, name, signature); PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table); uint32_t position; - static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert"); + static_assert(sizeof(uint32_t) == sizeof(_next_index)); if (lookup.search(comp, &position, reinterpret_cast(&_next_index))) { _r.set_position(static_cast(position)); return _next_index; diff --git a/src/hotspot/share/oops/flatArrayKlass.cpp b/src/hotspot/share/oops/flatArrayKlass.cpp index 0c5f5f320980..480d84fb003d 100644 --- a/src/hotspot/share/oops/flatArrayKlass.cpp +++ b/src/hotspot/share/oops/flatArrayKlass.cpp @@ -61,7 +61,7 @@ FlatArrayKlass::FlatArrayKlass(Klass* element_klass, Symbol* name, ArrayProperties props, LayoutKind lk) : ObjArrayKlass(1, element_klass, name, Kind, props), _layout_kind(lk) { - assert(element_klass->is_value_klass(), "Expected Inline"); + assert(element_klass->is_value_klass(), "Expected value klass"); assert(lk != LayoutKind::NULLABLE_NON_ATOMIC_FLAT, "Layout not supported by arrays yet (needs frozen arrays)"); assert(LayoutKindHelper::is_flat(lk), "Must be a flat layout"); diff --git a/src/hotspot/share/oops/flatArrayOop.hpp b/src/hotspot/share/oops/flatArrayOop.hpp index 01d1274ba740..4199f8b1111e 100644 --- a/src/hotspot/share/oops/flatArrayOop.hpp +++ b/src/hotspot/share/oops/flatArrayOop.hpp @@ -78,6 +78,6 @@ class flatArrayOopDesc : public objArrayOopDesc { }; // See similar requirement for oopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_VM_OOPS_FLATARRAYOOP_HPP diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 9e9df4b82ac6..357c20d96ff5 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -616,8 +616,8 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind, mark if (parser.is_hidden()) set_is_hidden(); set_layout_helper(Klass::instance_layout_helper(parser.layout_size(), false)); - if (parser.has_inlined_fields()) { - set_has_inlined_fields(); + if (parser.has_flat_fields()) { + set_has_flat_fields(); } assert(nullptr == _methods, "underlying memory not zeroed?"); @@ -4114,8 +4114,6 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le // ----------------------------------------------------------------------------------------------------- // Printing -#define BULLET " - " - static const char* state_names[] = { "allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error" }; @@ -4162,14 +4160,14 @@ void InstanceKlass::print_on(outputStream* st) const { assert(is_klass(), "must be klass"); Klass::print_on(st); - st->print(BULLET"instance size: %d", size_helper()); st->cr(); - st->print(BULLET"klass size: %d", size()); st->cr(); - st->print(BULLET"access: "); print_class_flags(st); st->cr(); - st->print(BULLET"flags: "); _misc_flags.print_on(st); st->cr(); - st->print(BULLET"state: "); st->print_cr("%s", init_state_name()); - st->print(BULLET"name: "); name()->print_value_on(st); st->cr(); - st->print(BULLET"super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr(); - st->print(BULLET"sub: "); + st->print(" - instance size: %d", size_helper()); st->cr(); + st->print(" - klass size: %d", size()); st->cr(); + st->print(" - access: "); print_class_flags(st); st->cr(); + st->print(" - flags: "); _misc_flags.print_on(st); st->cr(); + st->print(" - state: "); st->print_cr("%s", init_state_name()); + st->print(" - name: "); name()->print_value_on(st); st->cr(); + st->print(" - super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr(); + st->print(" - sub: "); Klass* sub = subklass(); int n; for (n = 0; sub != nullptr; n++, sub = sub->next_sibling()) { @@ -4182,47 +4180,47 @@ void InstanceKlass::print_on(outputStream* st) const { st->cr(); if (is_interface()) { - st->print_cr(BULLET"nof implementors: %d", nof_implementors()); + st->print_cr(" - nof implementors: %d", nof_implementors()); if (nof_implementors() == 1) { - st->print_cr(BULLET"implementor: "); + st->print_cr(" - implementor: "); st->print(" "); implementor()->print_value_on(st); st->cr(); } } - st->print(BULLET"arrays: "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr(); - st->print(BULLET"methods: "); + st->print(" - arrays: "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr(); + st->print(" - methods: "); print_array_on(st, methods(), [](outputStream* ost, Method* method) { method->print_value_on(ost); }); - st->print(BULLET"method ordering: "); + st->print(" - method ordering: "); print_array_on(st, method_ordering(), [](outputStream* ost, int i) { ost->print("%d", i); }); if (default_methods() != nullptr) { - st->print(BULLET"default_methods: "); + st->print(" - default_methods: "); print_array_on(st, default_methods(), [](outputStream* ost, Method* method) { method->print_value_on(ost); }); } - print_on_maybe_null(st, BULLET"default vtable indices: ", default_vtable_indices()); - st->print(BULLET"local interfaces: "); local_interfaces()->print_value_on(st); st->cr(); - st->print(BULLET"trans. interfaces: "); transitive_interfaces()->print_value_on(st); st->cr(); + print_on_maybe_null(st, " - default vtable indices: ", default_vtable_indices()); + st->print(" - local interfaces: "); local_interfaces()->print_value_on(st); st->cr(); + st->print(" - trans. interfaces: "); transitive_interfaces()->print_value_on(st); st->cr(); - st->print(BULLET"secondary supers: "); secondary_supers()->print_value_on(st); st->cr(); + st->print(" - secondary supers: "); secondary_supers()->print_value_on(st); st->cr(); - st->print(BULLET"hash_slot: %d", hash_slot()); st->cr(); - st->print(BULLET"secondary bitmap: " UINTX_FORMAT_X_0, _secondary_supers_bitmap); st->cr(); + st->print(" - hash_slot: %d", hash_slot()); st->cr(); + st->print(" - secondary bitmap: " UINTX_FORMAT_X_0, _secondary_supers_bitmap); st->cr(); if (secondary_supers() != nullptr) { if (Verbose) { bool is_hashed = (_secondary_supers_bitmap != SECONDARY_SUPERS_BITMAP_FULL); - st->print_cr(BULLET"---- secondary supers (%d words):", _secondary_supers->length()); + st->print_cr(" - ---- secondary supers (%d words):", _secondary_supers->length()); for (int i = 0; i < _secondary_supers->length(); i++) { ResourceMark rm; // for external_name() Klass* secondary_super = _secondary_supers->at(i); - st->print(BULLET"%2d:", i); + st->print(" - %2d:", i); if (is_hashed) { int home_slot = compute_home_slot(secondary_super, _secondary_supers_bitmap); int distance = (i - home_slot) & SECONDARY_SUPERS_TABLE_MASK; @@ -4232,19 +4230,19 @@ void InstanceKlass::print_on(outputStream* st) const { } } } - st->print(BULLET"constants: "); constants()->print_value_on(st); st->cr(); + st->print(" - constants: "); constants()->print_value_on(st); st->cr(); - print_on_maybe_null(st, BULLET"class loader data: ", class_loader_data()); - print_on_maybe_null(st, BULLET"source file: ", source_file_name()); + print_on_maybe_null(st, " - class loader data: ", class_loader_data()); + print_on_maybe_null(st, " - source file: ", source_file_name()); if (source_debug_extension() != nullptr) { - st->print(BULLET"source debug extension: "); + st->print(" - source debug extension: "); st->print("%s", source_debug_extension()); st->cr(); } - print_on_maybe_null(st, BULLET"class annotations: ", class_annotations()); - print_on_maybe_null(st, BULLET"class type annotations: ", class_type_annotations()); - print_on_maybe_null(st, BULLET"field annotations: ", fields_annotations()); - print_on_maybe_null(st, BULLET"field type annotations: ", fields_type_annotations()); + print_on_maybe_null(st, " - class annotations: ", class_annotations()); + print_on_maybe_null(st, " - class type annotations: ", class_type_annotations()); + print_on_maybe_null(st, " - field annotations: ", fields_annotations()); + print_on_maybe_null(st, " - field type annotations: ", fields_type_annotations()); { bool have_pv = false; // previous versions are linked together through the InstanceKlass @@ -4252,40 +4250,40 @@ void InstanceKlass::print_on(outputStream* st) const { pv_node != nullptr; pv_node = pv_node->previous_versions()) { if (!have_pv) - st->print(BULLET"previous version: "); + st->print(" - previous version: "); have_pv = true; pv_node->constants()->print_value_on(st); } if (have_pv) st->cr(); } - print_on_maybe_null(st, BULLET"generic signature: ", generic_signature()); - st->print(BULLET"inner classes: "); inner_classes()->print_value_on(st); st->cr(); - st->print(BULLET"nest members: "); nest_members()->print_value_on(st); st->cr(); - print_on_maybe_null(st, BULLET"record components: ", record_components()); - st->print(BULLET"permitted subclasses: "); permitted_subclasses()->print_value_on(st); st->cr(); - st->print(BULLET"loadable descriptors: "); loadable_descriptors()->print_value_on(st); st->cr(); + print_on_maybe_null(st, " - generic signature: ", generic_signature()); + st->print(" - inner classes: "); inner_classes()->print_value_on(st); st->cr(); + st->print(" - nest members: "); nest_members()->print_value_on(st); st->cr(); + print_on_maybe_null(st, " - record components: ", record_components()); + st->print(" - permitted subclasses: "); permitted_subclasses()->print_value_on(st); st->cr(); + st->print(" - loadable descriptors: "); loadable_descriptors()->print_value_on(st); st->cr(); if (java_mirror() != nullptr) { - st->print(BULLET"java mirror: "); + st->print(" - java mirror: "); java_mirror()->print_value_on(st); st->cr(); } else { - st->print_cr(BULLET"java mirror: null"); + st->print_cr(" - java mirror: null"); } - st->print(BULLET"vtable length %d (start addr: " PTR_FORMAT ")", vtable_length(), p2i(start_of_vtable())); st->cr(); + st->print(" - vtable length %d (start addr: " PTR_FORMAT ")", vtable_length(), p2i(start_of_vtable())); st->cr(); if (vtable_length() > 0 && (Verbose || WizardMode)) print_vtable(start_of_vtable(), vtable_length(), st); - st->print(BULLET"itable length %d (start addr: " PTR_FORMAT ")", itable_length(), p2i(start_of_itable())); st->cr(); + st->print(" - itable length %d (start addr: " PTR_FORMAT ")", itable_length(), p2i(start_of_itable())); st->cr(); if (itable_length() > 0 && (Verbose || WizardMode)) print_vtable(start_of_itable(), itable_length(), st); InstanceKlass* ik = const_cast(this); // There is no oop so static and nonstatic printing can use the same printer. FieldPrinter field_printer(st); - st->print_cr(BULLET"---- static fields (%d words):", static_field_size()); + st->print_cr(" - ---- static fields (%d words):", static_field_size()); ik->do_local_static_fields(&field_printer); - st->print_cr(BULLET"---- non-static fields (%d words):", nonstatic_field_size()); + st->print_cr(" - ---- non-static fields (%d words):", nonstatic_field_size()); ik->print_nonstatic_fields(&field_printer); - st->print(BULLET"non-static oop maps (%d entries): ", nonstatic_oop_map_count()); + st->print(" - non-static oop maps (%d entries): ", nonstatic_oop_map_count()); OopMapBlock* map = start_of_nonstatic_oop_maps(); OopMapBlock* end_map = map + nonstatic_oop_map_count(); while (map < end_map) { @@ -4295,7 +4293,7 @@ void InstanceKlass::print_on(outputStream* st) const { st->cr(); if (fieldinfo_search_table() != nullptr) { - st->print_cr(BULLET"---- field info search table:"); + st->print_cr(" - ---- field info search table:"); FieldInfoStream::print_search_table(st, _constants, _fieldinfo_stream, _fieldinfo_search_table); } } @@ -4308,7 +4306,7 @@ void InstanceKlass::print_value_on(outputStream* st) const { void FieldPrinter::do_field(fieldDescriptor* fd) { for (int i = 0; i < _indent; i++) _st->print(" "); - _st->print(BULLET); + _st->print(" - "); // Handles the cases of static fields or instance fields but no oop is given. if (_obj == nullptr) { fd->print_on(_st, _base_offset); @@ -4329,27 +4327,27 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st, int indent, int base if (value != nullptr && value->is_typeArray() && length <= (juint) value->length()) { - st->print(BULLET"string: "); + st->print(" - string: "); java_lang_String::print(obj, st); st->cr(); } } - st->print_cr(BULLET"---- fields (total size %zu words):", oop_size(obj)); + st->print_cr(" - ---- fields (total size %zu words):", oop_size(obj)); FieldPrinter print_field(st, obj, indent, base_offset); print_nonstatic_fields(&print_field); if (this == vmClasses::Class_klass()) { - st->print(BULLET"signature: "); + st->print(" - signature: "); java_lang_Class::print_signature(obj, st); st->cr(); Klass* real_klass = java_lang_Class::as_Klass(obj); if (real_klass != nullptr && real_klass->is_instance_klass()) { - st->print_cr(BULLET"---- static fields (%d):", java_lang_Class::static_oop_field_count(obj)); + st->print_cr(" - ---- static fields (%d):", java_lang_Class::static_oop_field_count(obj)); InstanceKlass::cast(real_klass)->do_local_static_fields(&print_field); } } else if (this == vmClasses::MethodType_klass()) { - st->print(BULLET"signature: "); + st->print(" - signature: "); java_lang_invoke_MethodType::print_signature(obj, st); st->cr(); } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 3d567f072b8a..471a592f1091 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -142,6 +142,8 @@ class OopMapBlock { struct JvmtiCachedClassFileData; class ValueFieldLayoutInfo : public MetaspaceObj { + friend class VMStructs; + ValueKlass* _klass; LayoutKind _kind; int _null_marker_offset; // null marker offset for this field, relative to the beginning of the current container @@ -384,8 +386,8 @@ class InstanceKlass: public Klass { bool has_localvariable_table() const { return _misc_flags.has_localvariable_table(); } void set_has_localvariable_table(bool b) { _misc_flags.set_has_localvariable_table(b); } - bool has_inlined_fields() const { return _misc_flags.has_inlined_fields(); } - void set_has_inlined_fields() { _misc_flags.set_has_inlined_fields(true); } + bool has_flat_fields() const { return _misc_flags.has_flat_fields(); } + void set_has_flat_fields() { _misc_flags.set_has_flat_fields(true); } bool has_null_restricted_static_fields() const { return _misc_flags.has_null_restricted_static_fields(); } void set_has_null_restricted_static_fields() { _misc_flags.set_has_null_restricted_static_fields(true); } diff --git a/src/hotspot/share/oops/instanceKlass.inline.hpp b/src/hotspot/share/oops/instanceKlass.inline.hpp index 7d9f789f0f5d..0fcc0daa0534 100644 --- a/src/hotspot/share/oops/instanceKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceKlass.inline.hpp @@ -69,7 +69,7 @@ inline address InstanceKlass::end_of_instance_klass() const { } inline ValueKlass* InstanceKlass::get_value_type_field_klass(int idx) const { - assert(has_inlined_fields() || has_null_restricted_static_fields(), "Sanity checking"); + assert(has_flat_fields() || has_null_restricted_static_fields(), "Sanity checking"); assert(idx < java_fields_count(), "IOOB"); ValueKlass* k = value_field_layout_info(idx).klass(); assert(k != nullptr, "Should always be set before being read"); @@ -77,7 +77,7 @@ inline ValueKlass* InstanceKlass::get_value_type_field_klass(int idx) const { } inline ValueKlass* InstanceKlass::get_value_type_field_klass_or_null(int idx) const { - assert(has_inlined_fields() || has_null_restricted_static_fields(), "Sanity checking"); + assert(has_flat_fields() || has_null_restricted_static_fields(), "Sanity checking"); assert(idx < java_fields_count(), "IOOB"); ValueKlass* k = value_field_layout_info(idx).klass(); return k; diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index f5054d8ae3b1..1c8c2527c644 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -53,7 +53,7 @@ class InstanceKlassFlags { flag(has_localvariable_table , 1 << 11) /* has localvariable information */ \ flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ - flag(has_inlined_fields , 1 << 14) /* has inlined fields and related embedded section is not empty */ \ + flag(has_flat_fields , 1 << 14) /* has flat fields and related embedded section is not empty */ \ flag(is_empty_value_type , 1 << 15) /* empty value type (*) */ \ flag(is_naturally_atomic , 1 << 16) /* loaded/stored in one instruction*/ \ flag(must_be_atomic , 1 << 17) /* doesn't allow tearing */ \ diff --git a/src/hotspot/share/oops/instanceOop.hpp b/src/hotspot/share/oops/instanceOop.hpp index 8baeeeb0f86b..491b6ff1513f 100644 --- a/src/hotspot/share/oops/instanceOop.hpp +++ b/src/hotspot/share/oops/instanceOop.hpp @@ -35,6 +35,6 @@ class instanceOopDesc : public oopDesc { }; // See similar requirement for oopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_INSTANCEOOP_HPP diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 0857a2ec158f..0bb1980333f7 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -1002,8 +1002,6 @@ void Klass::print_on(outputStream* st) const { st->cr(); } -#define BULLET " - " - // Caller needs ResourceMark void Klass::oop_print_on(oop obj, outputStream* st) { // print title @@ -1014,14 +1012,14 @@ void Klass::oop_print_on(oop obj, outputStream* st) { // print header obj->mark().print_on(st); st->cr(); - st->print(BULLET"prototype_header: " INTPTR_FORMAT, _prototype_header.value()); + st->print(" - prototype_header: " INTPTR_FORMAT, _prototype_header.value()); st->cr(); } // print class - st->print(BULLET"klass: "); + st->print(" - klass: "); obj->klass()->print_value_on(st); - st->print(BULLET"flags: "); _misc_flags.print_on(st); st->cr(); + st->print(" - flags: "); _misc_flags.print_on(st); st->cr(); st->cr(); } diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index b85cf956133d..7615a09d5782 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -26,9 +26,9 @@ #include "utilities/ostream.hpp" #ifdef _LP64 -STATIC_ASSERT(markWord::klass_shift + markWord::klass_bits == 64); +static_assert(markWord::klass_shift + markWord::klass_bits == 64); // The hash (preceding klass bits) shall be a direct neighbor but not interleave -STATIC_ASSERT(markWord::klass_shift == markWord::hash_bits + markWord::hash_shift); +static_assert(markWord::klass_shift == markWord::hash_bits + markWord::hash_shift); #endif void markWord::print_on(outputStream* st) const { diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 7a267bff6b55..38cd0722e881 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -206,7 +206,7 @@ class markWord { } // lock accessors (note that these assume lock_shift == 0) - STATIC_ASSERT(lock_shift == 0); + static_assert(lock_shift == 0); bool is_lock_neutral() const { return (mask_bits(value(), lock_mask_in_place) == lock_neutral_value); diff --git a/src/hotspot/share/oops/methodData.hpp b/src/hotspot/share/oops/methodData.hpp index 443f10512946..337a4badf52b 100644 --- a/src/hotspot/share/oops/methodData.hpp +++ b/src/hotspot/share/oops/methodData.hpp @@ -2235,7 +2235,7 @@ class MethodData : public Metadata { #ifndef ZERO // Some Zero platforms do not have expected alignment, and do not use // this code. static_assert would still fire and fail for them. - static_assert(sizeof(_trap_hist) % HeapWordSize == 0, "align"); + static_assert(sizeof(_trap_hist) % HeapWordSize == 0); #endif uint size_in_words = sizeof(_trap_hist) / HeapWordSize; Copy::zero_to_words((HeapWord*) &_trap_hist, size_in_words); diff --git a/src/hotspot/share/oops/objArrayOop.hpp b/src/hotspot/share/oops/objArrayOop.hpp index 2f0d84610827..80c4d5d13923 100644 --- a/src/hotspot/share/oops/objArrayOop.hpp +++ b/src/hotspot/share/oops/objArrayOop.hpp @@ -81,6 +81,6 @@ class objArrayOopDesc : public arrayOopDesc { }; // See similar requirement for oopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_OBJARRAYOOP_HPP diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index f810c07b956b..876026291fa0 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -358,6 +358,6 @@ class oopDesc { // to fill in certain parts of that memory. The allocated memory is then // treated as referring to an oopDesc. For that to be valid, the oopDesc // class must have a trivial default constructor (C++14 3.8/1). -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_OOP_HPP diff --git a/src/hotspot/share/oops/refArrayOop.hpp b/src/hotspot/share/oops/refArrayOop.hpp index d063c034d6e2..6e9aa629d032 100644 --- a/src/hotspot/share/oops/refArrayOop.hpp +++ b/src/hotspot/share/oops/refArrayOop.hpp @@ -88,6 +88,6 @@ class refArrayOopDesc : public objArrayOopDesc { }; // See similar requirement for oopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_REFARRAYOOP_HPP diff --git a/src/hotspot/share/oops/resolvedFieldEntry.cpp b/src/hotspot/share/oops/resolvedFieldEntry.cpp index f1f6a7143b1c..da4dc7a638b2 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.cpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotArtifactFinder.hpp" #include "cds/archiveBuilder.hpp" #include "cppstdlib/type_traits.hpp" #include "interpreter/bytecodes.hpp" @@ -98,6 +99,11 @@ void ResolvedFieldEntry::remove_unshareable_info() { *this = ResolvedFieldEntry(_cpool_index); } +// Called from AOTArtifactFinder. +void ResolvedFieldEntry::record_archivable_classes() { + AOTArtifactFinder::add_cached_class(_field_holder); +} + void ResolvedFieldEntry::mark_and_relocate() { ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_field_holder); } diff --git a/src/hotspot/share/oops/resolvedFieldEntry.hpp b/src/hotspot/share/oops/resolvedFieldEntry.hpp index d3afbb72e230..716a75efbf3a 100644 --- a/src/hotspot/share/oops/resolvedFieldEntry.hpp +++ b/src/hotspot/share/oops/resolvedFieldEntry.hpp @@ -159,6 +159,7 @@ class ResolvedFieldEntry { // CDS #if INCLUDE_CDS void remove_unshareable_info(); + void record_archivable_classes(); void mark_and_relocate(); #endif diff --git a/src/hotspot/share/oops/resolvedIndyEntry.cpp b/src/hotspot/share/oops/resolvedIndyEntry.cpp index 09060596caae..0ed9c8d48dfd 100644 --- a/src/hotspot/share/oops/resolvedIndyEntry.cpp +++ b/src/hotspot/share/oops/resolvedIndyEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * */ +#include "cds/aotArtifactFinder.hpp" #include "cds/archiveBuilder.hpp" #include "code/compressedStream.hpp" #include "oops/method.hpp" @@ -46,6 +47,12 @@ void ResolvedIndyEntry::remove_unshareable_info() { _cpool_index = saved_cpool_index; } +// Called from AOTArtifactFinder. +void ResolvedIndyEntry::record_archivable_classes() { + assert(is_resolved(), "must be"); + AOTArtifactFinder::add_cached_class(_method->method_holder()); +} + void ResolvedIndyEntry::mark_and_relocate() { assert(is_resolved(), "must be"); ArchiveBuilder::current()->mark_and_relocate_to_buffered_addr(&_method); diff --git a/src/hotspot/share/oops/resolvedIndyEntry.hpp b/src/hotspot/share/oops/resolvedIndyEntry.hpp index 04f46a42e8cb..1e4dc8789610 100644 --- a/src/hotspot/share/oops/resolvedIndyEntry.hpp +++ b/src/hotspot/share/oops/resolvedIndyEntry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,6 +134,7 @@ class ResolvedIndyEntry { // CDS #if INCLUDE_CDS void remove_unshareable_info(); + void record_archivable_classes(); void mark_and_relocate(); #endif diff --git a/src/hotspot/share/oops/resolvedMethodEntry.cpp b/src/hotspot/share/oops/resolvedMethodEntry.cpp index bb96ca86012b..dcf7b6cdc34f 100644 --- a/src/hotspot/share/oops/resolvedMethodEntry.cpp +++ b/src/hotspot/share/oops/resolvedMethodEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * */ +#include "cds/aotArtifactFinder.hpp" #include "cds/archiveBuilder.hpp" #include "cppstdlib/type_traits.hpp" #include "oops/method.hpp" @@ -60,6 +61,18 @@ void ResolvedMethodEntry::remove_unshareable_info() { reset_entry(); } +// Called from AOTArtifactFinder. +void ResolvedMethodEntry::record_archivable_classes() { + if (_method == nullptr) { + assert(bytecode2() == Bytecodes::_invokevirtual, ""); + } else { + AOTArtifactFinder::add_cached_class(_method->method_holder()); + } + if (bytecode1() == Bytecodes::_invokeinterface) { + AOTArtifactFinder::add_cached_class(_entry_specific._interface_klass); + } +} + void ResolvedMethodEntry::mark_and_relocate(ConstantPool* src_cp) { if (_method == nullptr) { assert(bytecode2() == Bytecodes::_invokevirtual, ""); diff --git a/src/hotspot/share/oops/resolvedMethodEntry.hpp b/src/hotspot/share/oops/resolvedMethodEntry.hpp index 802cf252a6d3..f614a74be987 100644 --- a/src/hotspot/share/oops/resolvedMethodEntry.hpp +++ b/src/hotspot/share/oops/resolvedMethodEntry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -242,6 +242,7 @@ class ResolvedMethodEntry { // CDS #if INCLUDE_CDS void remove_unshareable_info(); + void record_archivable_classes(); void mark_and_relocate(ConstantPool* src_cp); #endif diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 65a5015350c4..99912dbc939f 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -43,7 +43,7 @@ Symbol* Symbol::_vm_symbols[vmSymbols::number_of_symbols()]; uint32_t Symbol::pack_hash_and_refcount(short hash, int refcount) { - STATIC_ASSERT(PERM_REFCOUNT == ((1 << 16) - 1)); + static_assert(PERM_REFCOUNT == ((1 << 16) - 1)); assert(refcount >= 0, "negative refcount"); assert(refcount <= PERM_REFCOUNT, "invalid refcount"); uint32_t hi = hash; diff --git a/src/hotspot/share/oops/typeArrayOop.hpp b/src/hotspot/share/oops/typeArrayOop.hpp index de020920db3b..d1f670693065 100644 --- a/src/hotspot/share/oops/typeArrayOop.hpp +++ b/src/hotspot/share/oops/typeArrayOop.hpp @@ -133,6 +133,6 @@ class typeArrayOopDesc : public arrayOopDesc { }; // See similar requirement for oopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_TYPEARRAYOOP_HPP diff --git a/src/hotspot/share/oops/valueKlass.cpp b/src/hotspot/share/oops/valueKlass.cpp index df12a90dd533..1f31c92c8e97 100644 --- a/src/hotspot/share/oops/valueKlass.cpp +++ b/src/hotspot/share/oops/valueKlass.cpp @@ -518,19 +518,17 @@ void ValueKlass::remove_unshareable_info() { #endif // CDS -#define BULLET " - " - void ValueKlass::print_on(outputStream* st) const { InstanceKlass::print_on(st); members().print_on(st); - st->print_cr(BULLET"---- LayoutKinds:"); + st->print_cr(" - ---- LayoutKinds:"); auto print_layout_kind = [&](LayoutKind lk) { if (is_layout_supported(lk)) { - st->print_cr(BULLET"%s layout: %d/%d", + st->print_cr(" - %s layout: %d/%d", LayoutKindHelper::layout_kind_as_string(lk), layout_size_in_bytes(lk), layout_alignment(lk)); } else { - st->print_cr(BULLET"%s layout: -/-", + st->print_cr(" - %s layout: -/-", LayoutKindHelper::layout_kind_as_string(lk)); } }; @@ -554,32 +552,30 @@ void ValueKlass::oop_verify_on(oop obj, outputStream* st) { } void ValueKlass::Members::print_on(outputStream* st) const { - st->print_cr(BULLET"---- value type members:"); - st->print(BULLET"extended signature registers: "); + st->print_cr(" - ---- value type members:"); + st->print(" - extended signature registers: "); InstanceKlass::print_array_on(st, _extended_sig, [](outputStream* ost, SigEntry pair){ pair.print_on(ost); }); - st->print(BULLET"return registers: "); + st->print(" - return registers: "); InstanceKlass::print_array_on(st, _return_regs, [](outputStream* ost, VMRegPair pair) { pair.print_on(ost); }); - st->print_cr(BULLET"pack handler: " PTR_FORMAT, p2i(_pack_handler)); - st->print_cr(BULLET"pack handler (jobject): " PTR_FORMAT, p2i(_pack_handler_jobject)); - st->print_cr(BULLET"unpack handler: " PTR_FORMAT, p2i(_unpack_handler)); - st->print_cr(BULLET"null reset offset: %d", _null_reset_value_offset); - st->print_cr(BULLET"payload offset: %d", _payload_offset); - st->print_cr(BULLET"payload size (bytes): %d", _payload_size_in_bytes); - st->print_cr(BULLET"payload alignment: %d", _payload_alignment); - st->print_cr(BULLET"null-free non-atomic size (bytes): %d", _null_free_non_atomic_size_in_bytes); - st->print_cr(BULLET"null-free non-atomic alignment: %d", _null_free_non_atomic_alignment); - st->print_cr(BULLET"null-free atomic size (bytes): %d", _null_free_atomic_size_in_bytes); - st->print_cr(BULLET"nullable atomic size (bytes): %d", _nullable_atomic_size_in_bytes); - st->print_cr(BULLET"nullable non-atomic size (bytes): %d", _nullable_non_atomic_size_in_bytes); - st->print_cr(BULLET"null marker offset: %d", _null_marker_offset); - st->print_cr(BULLET"fast acmp offset: %d", _fast_acmp_offset); - st->print_cr(BULLET"fast acmp mask: " INT64_FORMAT_X_0, _fast_acmp_mask); - st->print_cr(BULLET"fast hashcode offset: %d", _fast_hashcode_offset); - st->print_cr(BULLET"fast hashcode shift: %d", _fast_hashcode_shift); + st->print_cr(" - pack handler: " PTR_FORMAT, p2i(_pack_handler)); + st->print_cr(" - pack handler (jobject): " PTR_FORMAT, p2i(_pack_handler_jobject)); + st->print_cr(" - unpack handler: " PTR_FORMAT, p2i(_unpack_handler)); + st->print_cr(" - null reset offset: %d", _null_reset_value_offset); + st->print_cr(" - payload offset: %d", _payload_offset); + st->print_cr(" - payload size (bytes): %d", _payload_size_in_bytes); + st->print_cr(" - payload alignment: %d", _payload_alignment); + st->print_cr(" - null-free non-atomic size (bytes): %d", _null_free_non_atomic_size_in_bytes); + st->print_cr(" - null-free non-atomic alignment: %d", _null_free_non_atomic_alignment); + st->print_cr(" - null-free atomic size (bytes): %d", _null_free_atomic_size_in_bytes); + st->print_cr(" - nullable atomic size (bytes): %d", _nullable_atomic_size_in_bytes); + st->print_cr(" - nullable non-atomic size (bytes): %d", _nullable_non_atomic_size_in_bytes); + st->print_cr(" - null marker offset: %d", _null_marker_offset); + st->print_cr(" - fast acmp offset: %d", _fast_acmp_offset); + st->print_cr(" - fast acmp mask: " INT64_FORMAT_X_0, _fast_acmp_mask); + st->print_cr(" - fast hashcode offset: %d", _fast_hashcode_offset); + st->print_cr(" - fast hashcode shift: %d", _fast_hashcode_shift); } - -#undef BULLET diff --git a/src/hotspot/share/oops/valueKlass.hpp b/src/hotspot/share/oops/valueKlass.hpp index b39db26eda96..764ceaca1f23 100644 --- a/src/hotspot/share/oops/valueKlass.hpp +++ b/src/hotspot/share/oops/valueKlass.hpp @@ -78,6 +78,7 @@ class ValueKlass: public InstanceKlass { // class Members { friend class ValueKlass; + friend class VMStructs; // Addresses used for value type calling convention Array* _extended_sig; diff --git a/src/hotspot/share/oops/valueKlass.inline.hpp b/src/hotspot/share/oops/valueKlass.inline.hpp index c8525a87e3b5..201c4ce2f8f3 100644 --- a/src/hotspot/share/oops/valueKlass.inline.hpp +++ b/src/hotspot/share/oops/valueKlass.inline.hpp @@ -121,8 +121,8 @@ void ValueKlass::oop_iterate_value_payload_f(address payload, Function function) OopMapBlock* const end_map = map + nonstatic_oop_map_count(); // OopMap offsets are relative to an object header, but we are iterating over - // inlined value payloads, which often don't have an object header. Set up a - // synthetic object base that can be used by the oop map offset calculations. + // flattened value payloads, which often don't have an object header. Use a + // synthetic object base for the oop map offset calculations. const address synthetic_object_base = payload - payload_offset(); for (; map < end_map; map++) { @@ -150,8 +150,8 @@ inline void ValueKlass::oop_iterate_value_payload_bounded(address payload, OopCl T* const h = (T*) hi; // OopMap offsets are relative to an object header, but we are iterating over - // inlined value payloads, which often don't have an object header. Set up a - // synthetic object base that can be used by the oop map offset calculations. + // flattened value payloads, which often don't have an object header. Use a + // synthetic object base for the oop map offset calculations. const address synthetic_object_base = payload - payload_offset(); for (; map < end_map; map++) { diff --git a/src/hotspot/share/oops/valueOop.hpp b/src/hotspot/share/oops/valueOop.hpp index c76af42e5d99..7cc1ce310f77 100644 --- a/src/hotspot/share/oops/valueOop.hpp +++ b/src/hotspot/share/oops/valueOop.hpp @@ -33,6 +33,6 @@ class valueOopDesc : public instanceOopDesc { }; // See similar requirement for instanceOopDesc. -static_assert(std::is_trivially_default_constructible::value, "required"); +static_assert(std::is_trivially_default_constructible::value); #endif // SHARE_OOPS_VALUEOOP_HPP diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 78467569e2e7..aa523fe9be80 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -964,6 +964,17 @@ Node* OrINode::Identity(PhaseGVN* phase) { return in(1); } + // x | (y | x) => y | x + if (in(2)->Opcode() == Op_OrI && + (in(2)->in(1) == in(1) || in(2)->in(2) == in(1))) { + return in(2); + } + // (x | y) | x => x | y + if (in(1)->Opcode() == Op_OrI && + (in(1)->in(1) == in(2) || in(1)->in(2) == in(2))) { + return in(1); + } + return AddNode::Identity(phase); } @@ -1037,6 +1048,17 @@ Node* OrLNode::Identity(PhaseGVN* phase) { return in(1); } + // x | (y | x) => y | x + if (in(2)->Opcode() == Op_OrL && + (in(2)->in(1) == in(1) || in(2)->in(2) == in(1))) { + return in(2); + } + // (x | y) | x => x | y + if (in(1)->Opcode() == Op_OrL && + (in(1)->in(1) == in(2) || in(1)->in(2) == in(2))) { + return in(1); + } + return AddNode::Identity(phase); } diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index af832a029581..6d2a70f80cf0 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -743,14 +743,16 @@ range(1, 100) \ \ develop(uint, VerifyIterativeGVN, 0, \ - "Verify Iterative Global Value Numbering =FEDCBA, with:" \ - " F: verify IGVN method return invariants" \ - " E: verify node specific invariants" \ - " D: verify Node::Identity did not miss opportunities" \ - " C: verify Node::Ideal did not miss opportunities" \ - " B: verify that type(n) == n->Value() after IGVN" \ - " A: verify Def-Use modifications during IGVN" \ - "Each can be 0=off or 1=on") \ + "Verify Iterative Global Value Numbering. Set the corresponding " \ + "decimal place to 1 to enable a check, or 0 to disable it:\n" \ + "100000: verify IGVN method return invariants\n" \ + " 10000: verify node specific invariants\n" \ + " 1000: verify Node::Identity did not miss opportunities\n" \ + " 100: verify Node::Ideal did not miss opportunities\n" \ + " 10: verify that type(n) == n->Value() after IGVN\n" \ + " 1: verify Def-Use modifications during IGVN\n" \ + "Example: -XX:VerifyIterativeGVN=101 enables Node::Ideal and " \ + "Def-Use checks.") \ constraint(VerifyIterativeGVNConstraintFunc, AtParse) \ \ develop(bool, TraceCISCSpill, false, \ @@ -993,7 +995,7 @@ \ product(uint, HotCodeMaxSamplingMs, 15, EXPERIMENTAL, \ "Maximum sampling interval in milliseconds") \ - range(0, max_juint) \ + range(1, max_juint) \ \ product(uint, HotCodeCallLevel, 1, EXPERIMENTAL, \ "Number of levels of callees to relocate per candidate") \ diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 528c07350b2c..be98b80bb8bf 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -880,38 +880,17 @@ bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { // are not passed as arguments according to Escape Analysis. return false; } - if (t_oop->is_ptr_to_boxed_value()) { + if (t_oop->is_ptr_to_boxed_value() && is_CallStaticJava()) { ciKlass* boxing_klass = t_oop->is_instptr()->instance_klass(); - if (is_CallStaticJava() && as_CallStaticJava()->is_boxing_method()) { + if (as_CallStaticJava()->is_boxing_method()) { // Skip unrelated boxing methods. Node* proj = proj_out_or_null(TypeFunc::Parms); if ((proj == nullptr) || (phase->type(proj)->is_instptr()->instance_klass() != boxing_klass)) { return false; } } - if (is_CallJava() && as_CallJava()->method() != nullptr) { - ciMethod* meth = as_CallJava()->method(); - if (meth->is_getter()) { - return false; - } - // May modify (by reflection) if an boxing object is passed - // as argument or returned. - Node* proj = returns_pointer() ? proj_out_or_null(TypeFunc::Parms) : nullptr; - if (proj != nullptr) { - const TypeInstPtr* inst_t = phase->type(proj)->isa_instptr(); - if ((inst_t != nullptr) && (!inst_t->klass_is_exact() || - (inst_t->instance_klass() == boxing_klass))) { - return true; - } - } - const TypeTuple* d = tf()->domain_cc(); - for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { - const TypeInstPtr* inst_t = d->field_at(i)->isa_instptr(); - if ((inst_t != nullptr) && (!inst_t->klass_is_exact() || - (inst_t->instance_klass() == boxing_klass))) { - return true; - } - } + ciMethod* meth = as_CallStaticJava()->method(); + if (meth != nullptr && meth->is_getter()) { return false; } } diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 4e395996c34b..82c2e374d672 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -256,7 +256,7 @@ PhaseChaitin::PhaseChaitin(uint unique, PhaseCFG &cfg, Matcher &matcher, bool sc } // Squash the partially filled buckets together into the first one. - static_assert(NUMBUCKS >= 2, "must"); // If this isn't true then it'll mess up the squashing. + static_assert(NUMBUCKS >= 2); // If this isn't true then it'll mess up the squashing. Block** offset = &buckets[0][buckcnt[0]]; for (int i = 1; i < NUMBUCKS; i++) { ::memmove(offset, buckets[i], buckcnt[i]*sizeof(Block*)); @@ -2489,13 +2489,11 @@ void PhaseChaitin::dump_frame() const { tty->print_cr("return address"); } else if (reg >= begin_in_preserve) { // Preserved slots are present on x86 - if (return_addr == OptoReg::add(reg, VMRegImpl::slots_per_word)) + if (return_addr == OptoReg::add(reg, VMRegImpl::slots_per_word)) { tty->print_cr("saved fp register"); - else if (return_addr == OptoReg::add(reg, 2*VMRegImpl::slots_per_word) && - VerifyStackAtCalls) - tty->print_cr(" +VerifyStackAtCalls"); - else + } else { tty->print_cr("in_preserve"); + } } else if (stack_slot < fixed_slots) { tty->print("Fixed slot %d", OptoReg::reg2stack(reg)); if (stack_slot == stack_increment_slot) { diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index eb22718ed175..3c390f500ed2 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -5564,12 +5564,12 @@ Node* LibraryCallKit::get_hashcode_from_header(Node* header, RegionNode* unset_r * } * * cache_path: - * if header is not safe to read { goto inline_fast_path } + * if header is not safe to read { goto value_fast_path } * hash = read_hash_from_header() - * if hash is empty { goto inline_fast_path } + * if hash is empty { goto value_fast_path } * return hash * - * inline_fast_path: + * value_fast_path: * if not static { goto slow } * if not value object { goto slow } * if value klass has no fast path { goto slow } @@ -5590,7 +5590,7 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { _slow_path = 1, // Actually perform the runtime call _cache_path, // Get the hash from the header _null_path, // If object is null, hash is 0. - _inline_fast_path, // Fast path for value objects only (see ValueKlass::Members::_fast_hashcode_offset et seqq.) + _value_fast_path, // Fast path for value objects only (see ValueKlass::Members::_fast_hashcode_offset et seqq.) PATH_LIMIT, }; @@ -5631,11 +5631,11 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { } // We only go to the cache case code if we pass a number of guards. The paths which do - // not pass are accumulated in the inline_fast_path_region. The compute region tries + // not pass are accumulated in the value_fast_path_region. The compute region tries // to use the fast path for value types. That also needs a lot of guards to be met. // The paths which do not pass are accumulated in the slow_region, where we do the // runtime call, which is the last resort. - RegionNode* inline_fast_path_region = new RegionNode(1); + RegionNode* value_fast_path_region = new RegionNode(1); RegionNode* slow_region = new RegionNode(1); // If this is a virtual call, we generate a funny guard. We pull out @@ -5657,12 +5657,12 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { Node* no_ctrl = nullptr; Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); - Node* hash_val = get_hashcode_from_header(header, inline_fast_path_region); + Node* hash_val = get_hashcode_from_header(header, value_fast_path_region); result_val->init_req(_cache_path, hash_val); result_reg->init_req(_cache_path, control()); - set_control(_gvn.transform(inline_fast_path_region)); + set_control(_gvn.transform(value_fast_path_region)); IfNode* fast_path_iff = nullptr; if (!stopped()) { if (UseHashcodeFastPath && is_static && !_gvn.type(obj)->is_valueklassptr()) { @@ -5728,8 +5728,8 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { unmasked_result->init_req(3, result_long); Node* fast_path_result = AndI(_gvn.transform(unmasked_result), intcon(markWord::hash_mask)); - result_reg->init_req(_inline_fast_path, _gvn.transform(unmasked_region)); - result_val->init_req(_inline_fast_path, fast_path_result); + result_reg->init_req(_value_fast_path, _gvn.transform(unmasked_region)); + result_val->init_req(_value_fast_path, fast_path_result); } } } @@ -5743,8 +5743,8 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { result_mem->init_req(_null_path, init_mem); result_io ->init_req(_cache_path, i_o()); result_mem->init_req(_cache_path, init_mem); - result_io ->set_req(_inline_fast_path, i_o()); - result_mem ->set_req(_inline_fast_path, init_mem); + result_io ->set_req(_value_fast_path, i_o()); + result_mem ->set_req(_value_fast_path, init_mem); // Generate code for the slow case. We make a call to hashCode(). set_control(_gvn.transform(slow_region)); @@ -7768,6 +7768,11 @@ bool LibraryCallKit::inline_vectorizedHashCode() { return false; // Only intrinsify if mode argument is constant } + const TypeAryPtr* array_t = _gvn.type(array)->isa_aryptr(); + if (array_t == nullptr || array_t->elem() == Type::BOTTOM) { + return false; // failed input validation + } + array = must_be_not_null(array, true); BasicType bt = (BasicType)basic_type_t->get_con(); @@ -9135,6 +9140,11 @@ bool LibraryCallKit::inline_dilithiumAlmostInverseNtt() { } //------------------------------inline_dilithiumNttMult +// +// int sun.security.provider.ML_DSA.implDilithiumNttMult(int[] product, +// int[] coeffs1, +// int[] coeffs2) +// bool LibraryCallKit::inline_dilithiumNttMult() { address stubAddr; const char *stubName; @@ -9148,12 +9158,10 @@ bool LibraryCallKit::inline_dilithiumNttMult() { Node* result = argument(0); Node* ntta = argument(1); Node* nttb = argument(2); - Node* zetas = argument(3); result = must_be_not_null(result, true); ntta = must_be_not_null(ntta, true); nttb = must_be_not_null(nttb, true); - zetas = must_be_not_null(zetas, true); Node* result_start = array_element_address(result, intcon(0), T_INT); assert(result_start, "result is null"); @@ -10373,4 +10381,3 @@ bool LibraryCallKit::inline_fp16_operations(vmIntrinsics::ID id, int num_args) { set_result(box_fp16_value(float16_box_type, field, result)); return true; } - diff --git a/src/hotspot/share/opto/locknode.cpp b/src/hotspot/share/opto/locknode.cpp index 5616343e0c89..a8f2478ea299 100644 --- a/src/hotspot/share/opto/locknode.cpp +++ b/src/hotspot/share/opto/locknode.cpp @@ -223,7 +223,7 @@ void Parse::do_monitor_enter() { { // Synchronizing on a value type is not allowed - BuildCutout unless(this, value_type_test(obj, /* is_inline = */ false), PROB_MAX); + BuildCutout unless(this, value_type_test(obj, /* is_value = */ false), PROB_MAX); uncommon_trap_exact(Deoptimization::Reason_class_check, Deoptimization::Action_none); } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index a71b80b11a40..35316d3ee08e 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -224,7 +224,7 @@ Node *PhaseIdealLoop::get_early_ctrl_for_expensive(Node *n, Node* earliest) { if (nb_ctl_proj > 1) { break; } - assert(parent_ctl->is_Start() || parent_ctl->is_MemBar() || parent_ctl->is_Call(), "unexpected node"); + assert(parent_ctl->is_Start() || parent_ctl->is_MemBar() || parent_ctl->is_SafePoint(), "unexpected node"); assert(idom(ctl) == parent_ctl, "strange"); next = idom(parent_ctl); } @@ -3923,7 +3923,7 @@ const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, No // We may have encountered multiple if conditions, that have no // overlap, and produce an empty/top type. Returning nullptr // is conservative, it means we do not constrain the type, which - // will just prevent further optimiziations. + // will just prevent further optimizations. assert(join_t->empty(), "top"); return nullptr; } @@ -3932,7 +3932,7 @@ const TypeInt* CountedLoopConverter::filtered_type_from_dominators(Node* val, No } } pred = _phase->idom(pred); - if (pred == nullptr || pred == _phase->C->top()) { + if (pred == nullptr || pred == _phase->C->top() || pred == _phase->C->start()) { break; } // Stop if going beyond definition block of val diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 9269179bfbe4..321479fcc833 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -835,6 +835,18 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode can_eliminate = false; } } + } else if (use->is_ArrayCopy() && !use->in(ArrayCopyNode::Src)->is_top() && + use->in(ArrayCopyNode::Src)->get_ptr_type()->is_atomic() && + use->in(ArrayCopyNode::Src)->get_ptr_type()->is_flat() && + !use->in(ArrayCopyNode::Src) + ->get_ptr_type() + ->isa_aryptr() + ->elem() + ->value_klass() + ->is_naturally_atomic( + use->in(ArrayCopyNode::Src)->get_ptr_type()->is_null_free())) { + NOT_PRODUCT(fail_eliminate = "Array element requires atomicity"); + can_eliminate = false; } else if (use->is_ArrayCopy() && (use->as_ArrayCopy()->is_clonebasic() || use->as_ArrayCopy()->is_arraycopy_validated() || @@ -1002,15 +1014,18 @@ void PhaseMacroExpand::undo_previous_scalarizations(Unique_Node_List& safepoints #ifdef ASSERT // Verify if a value can be written into a field. - void verify_type_compatability(const Type* value_type, const Type* field_type) { + void verify_value_type_compatibility(const Type* value_type, const Type* field_type) { BasicType value_bt = value_type->basic_type(); BasicType field_bt = field_type->basic_type(); - // Primitive types must match. - if (is_java_primitive(value_bt) && value_bt == field_bt) { return; } + // Primitive types must match or have a matching reinterpreted variant + if (is_java_primitive(value_bt) && + (value_bt == field_bt || MemNode::get_reinterpret_variant(value_bt) == field_bt)) { + return; + } // I have been struggling to make a similar assert for non-primitive - // types. I we can add one in the future. For now, I just let them + // types. If we can add one in the future. For now, I just let them // pass without checks. // In particular, I was struggling with a value that came from a call, // and had only a non-null check CastPP. There was also a checkcast @@ -1058,7 +1073,7 @@ void PhaseMacroExpand::process_field_value_at_safepoint(const Type* field_type, value_worklist->push(field_val); } } - DEBUG_ONLY(verify_type_compatability(field_val->bottom_type(), field_type);) + DEBUG_ONLY(verify_value_type_compatibility(field_val->bottom_type(), field_type);) sfpt->add_req(field_val); } diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 3c361d3f210a..e2581a356978 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1552,6 +1552,20 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { return nullptr; } +BasicType MemNode::get_reinterpret_variant(BasicType bt) { + switch (bt) { + case T_INT: return T_FLOAT; + case T_FLOAT: return T_INT; + case T_LONG: return T_DOUBLE; + case T_DOUBLE: return T_LONG; + default: return T_ILLEGAL; + } +} + +bool MemNode::has_reinterpret_variant(const Type* vt) const { + return get_reinterpret_variant(value_basic_type()) == vt->basic_type(); +} + //----------------------is_instance_field_load_with_local_phi------------------ bool LoadNode::is_instance_field_load_with_local_phi(Node* ctrl) { if( in(Memory)->is_Phi() && in(Memory)->in(0) == ctrl && @@ -1680,18 +1694,6 @@ Node* LoadNode::convert_to_signed_load(PhaseGVN& gvn) { false /*require_atomic_access*/, is_unaligned_access(), is_mismatched_access()); } -bool LoadNode::has_reinterpret_variant(const Type* rt) { - BasicType bt = rt->basic_type(); - switch (Opcode()) { - case Op_LoadI: return (bt == T_FLOAT); - case Op_LoadL: return (bt == T_DOUBLE); - case Op_LoadF: return (bt == T_INT); - case Op_LoadD: return (bt == T_LONG); - - default: return false; - } -} - Node* LoadNode::convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt) { BasicType bt = rt->basic_type(); assert(has_reinterpret_variant(rt), "no reinterpret variant: %s %s", Name(), type2name(bt)); @@ -1712,18 +1714,6 @@ Node* LoadNode::convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt) { require_atomic_access, is_unaligned_access(), is_mismatched); } -bool StoreNode::has_reinterpret_variant(const Type* vt) { - BasicType bt = vt->basic_type(); - switch (Opcode()) { - case Op_StoreI: return (bt == T_FLOAT); - case Op_StoreL: return (bt == T_DOUBLE); - case Op_StoreF: return (bt == T_INT); - case Op_StoreD: return (bt == T_LONG); - - default: return false; - } -} - Node* StoreNode::convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt) { BasicType bt = vt->basic_type(); assert(has_reinterpret_variant(vt), "no reinterpret variant: %s %s", Name(), type2name(bt)); diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 3a44757b8934..437752beb896 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -185,6 +185,10 @@ class MemNode : public Node { #endif return new_node; } + + // Support for reinterpret variants used by StoreNode and LoadNode + static BasicType get_reinterpret_variant(BasicType bt); + bool has_reinterpret_variant(const Type* vt) const; }; // Analyze a MemNode to try to prove that it is independent from other memory accesses @@ -349,7 +353,6 @@ class LoadNode : public MemNode { Node* convert_to_unsigned_load(PhaseGVN& gvn); Node* convert_to_signed_load(PhaseGVN& gvn); - bool has_reinterpret_variant(const Type* rt); Node* convert_to_reinterpret_load(PhaseGVN& gvn, const Type* rt); ControlDependency control_dependency() const { return _control_dependency; } @@ -711,7 +714,6 @@ class StoreNode : public MemNode { // have all possible loads of the value stored been optimized away? bool value_never_loaded(PhaseValues* phase) const; - bool has_reinterpret_variant(const Type* vt); Node* convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt); MemBarNode* trailing_membar() const; diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index aeb775836131..0e44f3745d36 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -749,10 +749,10 @@ void PhaseOutput::set_sv_for_object_node(GrowableArray *objs, static jint array_description_value(const TypeAryPtr* ary_type) { ciArrayKlass* array_klass = ary_type->exact_klass()->as_array_klass(); - const bool is_element_inline = array_klass->element_klass()->is_value_klass(); + const bool is_element_value = array_klass->element_klass()->is_value_klass(); ArrayProperties properties = ArrayProperties::Default() - .with_null_restricted(is_element_inline && array_klass->is_elem_null_free()) - .with_non_atomic(is_element_inline && !array_klass->is_elem_atomic()); + .with_null_restricted(is_element_value && array_klass->is_elem_null_free()) + .with_non_atomic(is_element_value && !array_klass->is_elem_atomic()); LayoutKind layout_kind = LayoutKind::REFERENCE; Klass::KlassKind kind = Klass::RefArrayKlassKind; diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 363853e80979..8cee4a1a6b4f 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1618,7 +1618,7 @@ static bool counters_are_meaningful(int counter1, int counter2, int min) { } // check for integer overflow of the sum int64_t sum = (int64_t)counter1 + (int64_t)counter2; - STATIC_ASSERT(sizeof(counter1) < sizeof(sum)); + static_assert(sizeof(counter1) < sizeof(sum)); if (sum > INT_MAX) { return false; } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1d0d24a6635a..9806deba1a6f 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2791,6 +2791,10 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ if (use->is_MinMax()) { add_users_to_worklist_if(worklist, use, [](Node* u) { return u->is_MinMax(); }); } + // Check for A | (B | C) and (B | C) | A where A == B or A == C. + if (use_op == Op_OrI || use_op == Op_OrL) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { return u->Opcode() == use->Opcode(); }); + } auto enqueue_init_mem_projs = [&](ProjNode* proj) { add_users_to_worklist0(proj, worklist); }; diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 09eb5750d6d9..73be09257f67 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -86,9 +86,9 @@ class KnownBits { template class TypeIntPrototype { public: - static_assert(S(-1) < S(0), ""); - static_assert(U(-1) > U(0), ""); - static_assert(sizeof(S) == sizeof(U), ""); + static_assert(S(-1) < S(0)); + static_assert(U(-1) > U(0)); + static_assert(sizeof(S) == sizeof(U)); RangeInt _srange; RangeInt _urange; @@ -126,9 +126,9 @@ class TypeIntHelper { // with the bottom type template static U cardinality_from_bounds(const RangeInt& srange, const RangeInt& urange) { - static_assert(S(-1) < S(0), ""); - static_assert(U(-1) > U(0), ""); - static_assert(sizeof(S) == sizeof(U), ""); + static_assert(S(-1) < S(0)); + static_assert(U(-1) > U(0)); + static_assert(sizeof(S) == sizeof(U)); if (U(srange._lo) == urange._lo) { // srange is the same as urange diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index 4ba891bcb5ae..91484d5087a7 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -78,7 +78,7 @@ class RegMask { friend class RegMaskIterator; // RM_SIZE_IN_INTS is aligned to 64-bit - assert that this holds - LP64_ONLY(STATIC_ASSERT(is_aligned(RM_SIZE_IN_INTS, 2))); + LP64_ONLY(static_assert(is_aligned(RM_SIZE_IN_INTS, 2))); static const unsigned int WORD_BIT_MASK = BitsPerWord - 1U; @@ -105,11 +105,11 @@ class RegMask { LP64_ONLY(((RM_SIZE_IN_INTS_MAX + 1) & ~1) >> 1) NOT_LP64(RM_SIZE_IN_INTS_MAX); // Sanity check - STATIC_ASSERT(RM_SIZE_IN_INTS <= RM_SIZE_IN_INTS_MAX); + static_assert(RM_SIZE_IN_INTS <= RM_SIZE_IN_INTS_MAX); // Ensure that register masks cannot grow beyond the point at which // OptoRegPair can no longer index the whole mask - STATIC_ASSERT(OptoRegPair::can_fit((RM_SIZE_IN_INTS_MAX << 5) - 1)); + static_assert(OptoRegPair::can_fit((RM_SIZE_IN_INTS_MAX << 5) - 1)); union { // Array of Register Mask bits. The array should be diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 8365afeb72dd..2f318b2c3ce6 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -2267,11 +2267,11 @@ const TypeTuple *TypeTuple::LONG_PAIR; const TypeTuple *TypeTuple::INT_CC_PAIR; const TypeTuple *TypeTuple::LONG_CC_PAIR; -static void collect_inline_fields(ciValueKlass* vk, const Type** field_array, uint& pos) { +static void collect_value_fields(ciValueKlass* vk, const Type** field_array, uint& pos) { for (int i = 0; i < vk->nof_declared_nonstatic_fields(); i++) { ciField* field = vk->declared_nonstatic_field_at(i); if (field->is_flat()) { - collect_inline_fields(field->type()->as_value_klass(), field_array, pos); + collect_value_fields(field->type()->as_value_klass(), field_array, pos); if (!field->is_null_free()) { // Use T_INT instead of T_BOOLEAN here because the upper bits can contain garbage if the holder // is null and C2 will only zero them for T_INT assuming that T_BOOLEAN is already canonicalized. @@ -2314,7 +2314,7 @@ const TypeTuple* TypeTuple::make_range(ciSignature* sig, InterfaceHandling inter if (ret_vt_fields) { uint pos = TypeFunc::Parms; field_array[pos++] = get_const_type(return_type); // Oop might be null when returning as fields - collect_inline_fields(return_type->as_value_klass(), field_array, pos); + collect_value_fields(return_type->as_value_klass(), field_array, pos); if (is_call) { // ValueTypeNode::NullMarker field returned by scalarized calls field_array[pos++] = get_const_basic_type(T_BOOLEAN); @@ -2360,7 +2360,7 @@ const TypeTuple *TypeTuple::make_domain(ciMethod* method, InterfaceHandling inte ciInstanceKlass* recv = method->holder(); if (vt_fields_as_args && recv->is_value_klass() && recv->as_value_klass()->can_be_passed_as_fields() && method->is_scalarized_arg(0)) { field_array[pos++] = get_const_type(recv, interface_handling); // buffer argument - collect_inline_fields(recv->as_value_klass(), field_array, pos); + collect_value_fields(recv->as_value_klass(), field_array, pos); } else { field_array[pos++] = get_const_type(recv, interface_handling)->join_speculative(TypePtr::NOTNULL); } @@ -2385,7 +2385,7 @@ const TypeTuple *TypeTuple::make_domain(ciMethod* method, InterfaceHandling inte field_array[pos++] = get_const_type(type, interface_handling); // buffer argument // ValueTypeNode::NullMarker field used for null checking field_array[pos++] = get_const_basic_type(T_BOOLEAN); - collect_inline_fields(type->as_value_klass(), field_array, pos); + collect_value_fields(type->as_value_klass(), field_array, pos); } else { field_array[pos++] = get_const_type(type, interface_handling); } @@ -5922,7 +5922,7 @@ const Type* TypeInstKlassPtr::xjoin(const Type* t) const { } template bool TypePtr::is_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); if (!this_one->is_loaded() || !other->is_loaded()) { return false; } @@ -5959,7 +5959,7 @@ bool TypeInstKlassPtr::is_java_subtype_of_helper(const TypeKlassPtr* other, bool } template bool TypePtr::is_same_java_type_as_helper_for_instance(const T1* this_one, const T2* other) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); if (!this_one->is_loaded() || !other->is_loaded()) { return false; } @@ -5974,7 +5974,7 @@ bool TypeInstKlassPtr::is_same_java_type_as_helper(const TypeKlassPtr* other) co } template bool TypePtr::maybe_java_subtype_of_helper_for_instance(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); if (!this_one->is_loaded() || !other->is_loaded()) { return true; } @@ -6412,7 +6412,7 @@ const Type* TypeAryKlassPtr::xjoin(const Type* t) const { } template bool TypePtr::is_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); if (other->klass() == ciEnv::current()->Object_klass() && other->_interfaces->empty() && other_exact) { return true; @@ -6456,7 +6456,7 @@ bool TypeAryKlassPtr::is_java_subtype_of_helper(const TypeKlassPtr* other, bool } template bool TypePtr::is_same_java_type_as_helper_for_array(const T1* this_one, const T2* other) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); int dummy; bool this_top_or_bottom = (this_one->base_element_type(dummy) == Type::TOP || this_one->base_element_type(dummy) == Type::BOTTOM); @@ -6488,7 +6488,7 @@ bool TypeAryKlassPtr::is_same_java_type_as_helper(const TypeKlassPtr* other) con } template bool TypePtr::maybe_java_subtype_of_helper_for_array(const T1* this_one, const T2* other, bool this_exact, bool other_exact) { - static_assert(std::is_base_of::value, ""); + static_assert(std::is_base_of::value); if (other->klass() == ciEnv::current()->Object_klass() && other->_interfaces->empty() && other_exact) { return true; } diff --git a/src/hotspot/share/opto/valuetypenode.cpp b/src/hotspot/share/opto/valuetypenode.cpp index b27434189792..d219d08bf241 100644 --- a/src/hotspot/share/opto/valuetypenode.cpp +++ b/src/hotspot/share/opto/valuetypenode.cpp @@ -608,13 +608,13 @@ static bool check_cycle(ciValueKlass* vk) { // Check if 'lhs' and 'rhs' are the same oop, possibly wrapped in an ValueTypeNode. static bool same_oop(PhaseGVN* phase, Node* lhs, Node* rhs) { - ValueTypeNode* lhs_inline = lhs->isa_ValueType(); - if (lhs_inline != nullptr && lhs_inline->is_allocated(phase)) { - lhs = lhs_inline->get_oop(); + ValueTypeNode* lhs_value = lhs->isa_ValueType(); + if (lhs_value != nullptr && lhs_value->is_allocated(phase)) { + lhs = lhs_value->get_oop(); } - ValueTypeNode* rhs_inline = rhs->isa_ValueType(); - if (rhs_inline != nullptr && rhs_inline->is_allocated(phase)) { - rhs = rhs_inline->get_oop(); + ValueTypeNode* rhs_value = rhs->isa_ValueType(); + if (rhs_value != nullptr && rhs_value->is_allocated(phase)) { + rhs = rhs_value->get_oop(); } return lhs->eqv_uncast(rhs); } @@ -648,25 +648,25 @@ bool ValueTypeNode::can_emit_substitutability_check(PhaseGVN* phase, Node* lhs, swap(lhs, rhs); } - ValueTypeNode* lhs_inline = lhs->as_ValueType(); - ValueTypeNode* rhs_inline = rhs != nullptr ? rhs->isa_ValueType() : nullptr; - if (rhs_inline != nullptr && lhs_inline->type()->value_klass() != rhs_inline->type()->value_klass()) { + ValueTypeNode* lhs_value = lhs->as_ValueType(); + ValueTypeNode* rhs_value = rhs != nullptr ? rhs->isa_ValueType() : nullptr; + if (rhs_value != nullptr && lhs_value->type()->value_klass() != rhs_value->type()->value_klass()) { // Dead code, can skip the substitutability check return true; } - if (check_cycle(lhs_inline->value_klass())) { + if (check_cycle(lhs_value->value_klass())) { return false; } - for (uint i = 0; i < lhs_inline->field_count(); i++) { - ciType* ft = lhs_inline->field(i)->type(); + for (uint i = 0; i < lhs_value->field_count(); i++) { + ciType* ft = lhs_value->field(i)->type(); if (!ft->can_be_value_klass()) { continue; } - Node* lhs_fv = lhs_inline->field_value(i); - Node* rhs_fv = rhs_inline != nullptr ? rhs_inline->field_value(i) : nullptr; + Node* lhs_fv = lhs_value->field_value(i); + Node* rhs_fv = rhs_value != nullptr ? rhs_value->field_value(i) : nullptr; if (!can_emit_substitutability_check(phase, lhs_fv, rhs_fv)) { return false; } @@ -892,8 +892,8 @@ Node* ValueTypeNode::emit_substitutability_check(GraphKit* kit, Node* lhs, Node* Node* cur_lhs_field = cur_lhs->field_value(field_idx); Node* cur_rhs_field = cur_rhs->field_value(field_idx); - ValueTypeNode* cur_lhs_inline = cur_lhs_field->isa_ValueType(); - ValueTypeNode* cur_rhs_inline = cur_rhs_field->isa_ValueType(); + ValueTypeNode* cur_lhs_value = cur_lhs_field->isa_ValueType(); + ValueTypeNode* cur_rhs_value = cur_rhs_field->isa_ValueType(); Node* preprocess = emit_substitutability_check_pointer(kit, result, cur_lhs_field, cur_rhs_field); if (kit->stopped()) { @@ -981,8 +981,8 @@ Node* ValueTypeNode::emit_substitutability_check(GraphKit* kit, Node* lhs, Node* // Both must be vk // ValueTypeNodes need no (new) field loads, so we can use the uncasted value below. - if (cur_lhs_inline != nullptr && cur_lhs_inline->value_klass() == vk) { - cur_lhs_field = cur_lhs_inline; + if (cur_lhs_value != nullptr && cur_lhs_value->value_klass() == vk) { + cur_lhs_field = cur_lhs_value; } else { Node* not_vk = kit->top(); cur_lhs_field = kit->gen_checkcast(cur_lhs_field, vk_klass, ¬_vk); @@ -993,8 +993,8 @@ Node* ValueTypeNode::emit_substitutability_check(GraphKit* kit, Node* lhs, Node* cur_lhs_field = ValueTypeNode::make_from_oop(kit, cur_lhs_field, vk); } - if (cur_rhs_inline != nullptr && cur_rhs_inline->value_klass() == vk) { - cur_rhs_field = cur_rhs_inline; + if (cur_rhs_value != nullptr && cur_rhs_value->value_klass() == vk) { + cur_rhs_field = cur_rhs_value; } else { Node* not_vk = kit->top(); cur_rhs_field = kit->gen_checkcast(cur_rhs_field, vk_klass, ¬_vk); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index f1857409455a..4d258fc0c286 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -1392,11 +1392,17 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { addr = array_element_address(base, index, elem_bt); } - const TypePtr* addr_type = gvn().type(addr)->isa_ptr(); - const TypeAryPtr* arr_type = addr_type->isa_aryptr(); + const TypeAryPtr* arr_type = gvn().type(addr)->isa_aryptr(); - // The array must be consistent with vector type - if (arr_type == nullptr || (arr_type != nullptr && !elem_consistent_with_arr(elem_bt, arr_type, false))) { + // Gather and scatter address an array, and the array must be consistent with + // the vector type. + if (arr_type == nullptr) { + log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s atype=not an array", + is_scatter, is_scatter ? "scatter" : "gather", + num_elem, type2name(elem_bt)); + return false; + } + if (!elem_consistent_with_arr(elem_bt, arr_type, false)) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s atype=%s ismask=no", is_scatter, is_scatter ? "scatter" : "gather", num_elem, type2name(elem_bt), type2name(arr_type->elem()->array_element_basic_type())); @@ -1447,17 +1453,17 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { Node* vstore = nullptr; if (mask != nullptr) { - vstore = gvn().transform(trace_vector(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, addr_type, val, indexes, mask))); + vstore = gvn().transform(trace_vector(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, arr_type, val, indexes, mask))); } else { - vstore = gvn().transform(trace_vector(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, indexes))); + vstore = gvn().transform(trace_vector(new StoreVectorScatterNode(control(), memory(addr), addr, arr_type, val, indexes))); } - set_memory(vstore, addr_type); + set_memory(vstore, arr_type); } else { Node* vload = nullptr; if (mask != nullptr) { - vload = gvn().transform(trace_vector(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, addr_type, vector_type, indexes, mask))); + vload = gvn().transform(trace_vector(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, arr_type, vector_type, indexes, mask))); } else { - vload = gvn().transform(trace_vector(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, indexes))); + vload = gvn().transform(trace_vector(new LoadVectorGatherNode(control(), memory(addr), addr, arr_type, vector_type, indexes))); } Node* box = box_vector(vload, vbox_type, elem_bt, num_elem); set_result(box); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 6c0b8609a4fb..5046bb63f56a 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -2969,8 +2969,8 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { if (thread->should_hide_jvmti_events()) { return; } - const bool is_inline = object->is_value(); - if (is_inline && !JvmtiExport::can_support_value_objects()) { + const bool is_value = object->is_value(); + if (is_value && !JvmtiExport::can_support_value_objects()) { return; } HandleMark hm(thread); @@ -2981,7 +2981,7 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) { JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != nullptr; env = it.next(env)) { if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC) && - (!is_inline || env->get_capabilities()->can_support_value_objects != 0)) { + (!is_value || env->get_capabilities()->can_support_value_objects != 0)) { EVT_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Evt vmobject alloc sent %s", JvmtiTrace::safe_get_thread_name(thread), object->klass()->external_name())); @@ -3011,8 +3011,8 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { if (thread->should_hide_jvmti_events()) { return; } - const bool is_inline = object->is_value(); - if (is_inline && !JvmtiExport::can_support_value_objects()) { + const bool is_value = object->is_value(); + if (is_value && !JvmtiExport::can_support_value_objects()) { return; } @@ -3023,7 +3023,7 @@ void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) { for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { JvmtiEnv *env = ets->get_env(); if (ets->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) && - (!is_inline || env->get_capabilities()->can_support_value_objects != 0)) { + (!is_value || env->get_capabilities()->can_support_value_objects != 0)) { EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, ("[%s] Evt sampled object alloc sent %s", JvmtiTrace::safe_get_thread_name(thread), diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index 52e53340558f..a01c9c767269 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -484,7 +484,7 @@ void VM_BaseGetOrSetLocal::check_and_clone_this_value_object() { assert(_type == T_OBJECT, "sanity check"); assert(obj != nullptr, "expected non-null oop"); - assert(obj_h()->is_value(), "expected inline oop"); + assert(obj_h()->is_value(), "expected value oop"); assert(_index == 0, "expected slot 0 for THIS object"); ValueKlass* klass = ValueKlass::cast(obj_h()->klass()); diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index 5ab97765a8b7..fc15fce99169 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -1349,7 +1349,7 @@ void IterateThroughHeapObjectClosure::visit_object(const JvmtiHeapwalkObject& ob // If the object has flat fields, report them as heap objects. if (obj.klass()->is_instance_klass()) { - if (InstanceKlass::cast(obj.klass())->has_inlined_fields()) { + if (InstanceKlass::cast(obj.klass())->has_flat_fields()) { visit_flat_fields(obj); // check if iteration has been halted if (is_iteration_aborted()) { @@ -1376,7 +1376,7 @@ void IterateThroughHeapObjectClosure::visit_flat_fields(const JvmtiHeapwalkObjec int field_offset = field->field_offset(); if (obj.is_flat()) { - // the object is inlined, its fields are stored without the header + // the object is flattened, its fields are stored without the header field_offset += obj.offset() - obj.value_klass()->payload_offset(); } // check for possible nulls @@ -3109,7 +3109,7 @@ inline bool VM_HeapWalkOperation::iterate_over_object(const JvmtiHeapwalkObject& int slot = field->field_index(); int field_offset = field->field_offset(); if (o.is_flat()) { - // the object is inlined, its fields are stored without the header + // the object is flattened, its fields are stored without the header field_offset += o.offset() - o.value_klass()->payload_offset(); } if (!is_primitive_field_type(type)) { diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 10e1f98e9915..ff85014bf7c5 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -31,6 +31,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/vmOperations.hpp" #include "utilities/checkedCast.hpp" #include "utilities/globalDefinitions.hpp" @@ -49,6 +50,13 @@ struct UpcallContext { Thread* attachedThread; ~UpcallContext() { + if (VM_Exit::vm_exited()) { + // This thread terminated after VM exit + // and can't be detached anymore. + // Leaking it is preferable to deadlocking + // in DetachCurrentThread. + return; + } if (attachedThread != nullptr) { JavaVM_ *vm = (JavaVM *)(&main_vm); vm->functions->DetachCurrentThread(vm); diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index 894e04eec57d..233b6a7ecf7c 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -289,7 +289,7 @@ class AtomicImpl::SupportsArithmetic : public CommonCore { static_assert(std::is_integral_v, "offset must be integral"); static_assert(sizeof(Offset) <= sizeof(T), "offset size exceeds value size"); if constexpr (!std::is_integral_v) { - static_assert(std::is_pointer_v, "must be"); + static_assert(std::is_pointer_v); } else if constexpr (std::is_signed_v) { static_assert(std::is_signed_v, "value is signed but offset is unsigned"); diff --git a/src/hotspot/share/runtime/atomicAccess.hpp b/src/hotspot/share/runtime/atomicAccess.hpp index 46330cffdb26..d5022d21c3ba 100644 --- a/src/hotspot/share/runtime/atomicAccess.hpp +++ b/src/hotspot/share/runtime/atomicAccess.hpp @@ -532,7 +532,7 @@ struct AtomicAccess::LoadImpl< T operator()(T const volatile* dest) const { typedef PrimitiveConversions::Translate Translator; typedef typename Translator::Decayed Decayed; - STATIC_ASSERT(sizeof(T) == sizeof(Decayed)); + static_assert(sizeof(T) == sizeof(Decayed)); Decayed result = PlatformOp()(reinterpret_cast(dest)); return Translator::recover(result); } @@ -548,7 +548,7 @@ template struct AtomicAccess::PlatformLoad { template T operator()(T const volatile* dest) const { - STATIC_ASSERT(sizeof(T) <= sizeof(void*)); // wide atomics need specialization + static_assert(sizeof(T) <= sizeof(void*)); // wide atomics need specialization return *dest; } }; @@ -601,7 +601,7 @@ struct AtomicAccess::StoreImpl< void operator()(T volatile* dest, T new_value) const { typedef PrimitiveConversions::Translate Translator; typedef typename Translator::Decayed Decayed; - STATIC_ASSERT(sizeof(T) == sizeof(Decayed)); + static_assert(sizeof(T) == sizeof(Decayed)); PlatformOp()(reinterpret_cast(dest), Translator::decay(new_value)); } @@ -618,21 +618,21 @@ struct AtomicAccess::PlatformStore { template void operator()(T volatile* dest, T new_value) const { - STATIC_ASSERT(sizeof(T) <= sizeof(void*)); // wide atomics need specialization + static_assert(sizeof(T) <= sizeof(void*)); // wide atomics need specialization (void)const_cast(*dest = new_value); } }; template inline void AtomicAccess::inc(D volatile* dest, atomic_memory_order order) { - STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); + static_assert(std::is_pointer::value || std::is_integral::value); using I = std::conditional_t::value, ptrdiff_t, D>; AtomicAccess::add(dest, I(1), order); } template inline void AtomicAccess::dec(D volatile* dest, atomic_memory_order order) { - STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); + static_assert(std::is_pointer::value || std::is_integral::value); using I = std::conditional_t::value, ptrdiff_t, D>; // Assumes two's complement integer representation. AtomicAccess::add(dest, I(-1), order); @@ -640,15 +640,15 @@ inline void AtomicAccess::dec(D volatile* dest, atomic_memory_order order) { template inline D AtomicAccess::sub(D volatile* dest, I sub_value, atomic_memory_order order) { - STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); - STATIC_ASSERT(std::is_integral::value); + static_assert(std::is_pointer::value || std::is_integral::value); + static_assert(std::is_integral::value); // If D is a pointer type, use [u]intptr_t as the addend type, // matching signedness of I. Otherwise, use D as the addend type. using PI = std::conditional_t::value, intptr_t, uintptr_t>; using AddendType = std::conditional_t::value, PI, D>; // Only allow conversions that can't change the value. - STATIC_ASSERT(std::is_signed::value == std::is_signed::value); - STATIC_ASSERT(sizeof(I) <= sizeof(AddendType)); + static_assert(std::is_signed::value == std::is_signed::value); + static_assert(sizeof(I) <= sizeof(AddendType)); AddendType addend = sub_value; // Assumes two's complement integer representation. return AtomicAccess::add(dest, -addend, order); @@ -709,8 +709,8 @@ class AtomicAccess::AddUsingCmpxchg { static inline D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) { - STATIC_ASSERT(byte_size == sizeof(I)); - STATIC_ASSERT(byte_size == sizeof(D)); + static_assert(byte_size == sizeof(I)); + static_assert(byte_size == sizeof(D)); D old_value; D new_value; @@ -943,8 +943,8 @@ struct AtomicAccess::AddImpl< P*, I, typename EnableIf::value && (sizeof(I) <= sizeof(P*))>::type> { - STATIC_ASSERT(sizeof(intptr_t) == sizeof(P*)); - STATIC_ASSERT(sizeof(uintptr_t) == sizeof(P*)); + static_assert(sizeof(intptr_t) == sizeof(P*)); + static_assert(sizeof(uintptr_t) == sizeof(P*)); // Type of the scaled addend. An integral type of the same size as a // pointer, and the same signedness as I. @@ -1071,7 +1071,7 @@ struct AtomicAccess::CmpxchgImpl< atomic_memory_order order) const { typedef PrimitiveConversions::Translate Translator; typedef typename Translator::Decayed Decayed; - STATIC_ASSERT(sizeof(T) == sizeof(Decayed)); + static_assert(sizeof(T) == sizeof(Decayed)); return Translator::recover( cmpxchg(reinterpret_cast(dest), Translator::decay(compare_value), @@ -1085,7 +1085,7 @@ inline T AtomicAccess::cmpxchg_using_helper(Fn fn, T volatile* dest, T compare_value, T exchange_value) { - STATIC_ASSERT(sizeof(Type) == sizeof(T)); + static_assert(sizeof(Type) == sizeof(T)); return PrimitiveConversions::cast( fn(PrimitiveConversions::cast(exchange_value), reinterpret_cast(dest), @@ -1111,7 +1111,7 @@ inline T AtomicAccess::CmpxchgByteUsingInt::operator()(T volatile* dest, T compare_value, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(sizeof(T) == sizeof(uint8_t)); + static_assert(sizeof(T) == sizeof(uint8_t)); uint8_t canon_exchange_value = exchange_value; uint8_t canon_compare_value = compare_value; volatile uint32_t* aligned_dest @@ -1191,7 +1191,7 @@ struct AtomicAccess::XchgImpl< T operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { typedef PrimitiveConversions::Translate Translator; typedef typename Translator::Decayed Decayed; - STATIC_ASSERT(sizeof(T) == sizeof(Decayed)); + static_assert(sizeof(T) == sizeof(Decayed)); return Translator::recover( xchg(reinterpret_cast(dest), Translator::decay(exchange_value), @@ -1203,7 +1203,7 @@ template inline T AtomicAccess::xchg_using_helper(Fn fn, T volatile* dest, T exchange_value) { - STATIC_ASSERT(sizeof(Type) == sizeof(T)); + static_assert(sizeof(Type) == sizeof(T)); // Notice the swapped order of arguments. Change when/if stubs are rewritten. return PrimitiveConversions::cast( fn(PrimitiveConversions::cast(exchange_value), @@ -1220,7 +1220,7 @@ template inline T AtomicAccess::XchgUsingCmpxchg::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { - STATIC_ASSERT(byte_size == sizeof(T)); + static_assert(byte_size == sizeof(T)); T old_value; do { diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 4a8c1f79a64e..9f79dee63460 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -74,6 +74,9 @@ #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" #include "utilities/vmError.hpp" +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/shenandoahStackChunkGCData.inline.hpp" +#endif #if INCLUDE_ZGC #include "gc/z/zStackChunkGCData.inline.hpp" #endif @@ -1614,6 +1617,7 @@ stackChunkOop Freeze::allocate_chunk(size_t stack_size, int argsize_md) #endif #if INCLUDE_SHENANDOAHGC if (UseShenandoahGC) { + ShenandoahStackChunkGCData::initialize(chunk); _barriers = chunk->requires_barriers(); } else #endif diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d83b56f5ef4f..b661acf488c7 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -2488,7 +2488,7 @@ const char* Deoptimization::_trap_action_name[] = { const char* Deoptimization::trap_reason_name(int reason) { // Check that every reason has a name - STATIC_ASSERT(sizeof(_trap_reason_name)/sizeof(const char*) == Reason_LIMIT); + static_assert(sizeof(_trap_reason_name)/sizeof(const char*) == Reason_LIMIT); if (reason == Reason_many) return "many"; if ((uint)reason < Reason_LIMIT) @@ -2499,7 +2499,7 @@ const char* Deoptimization::trap_reason_name(int reason) { } const char* Deoptimization::trap_action_name(int action) { // Check that every action has a name - STATIC_ASSERT(sizeof(_trap_action_name)/sizeof(const char*) == Action_LIMIT); + static_assert(sizeof(_trap_action_name)/sizeof(const char*) == Action_LIMIT); if ((uint)action < Action_LIMIT) return _trap_action_name[action]; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 99af89050fdb..703b0776cadd 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -455,9 +455,6 @@ const int ObjectAlignmentInBytes = 8; product(bool, BytecodeVerificationLocal, false, DIAGNOSTIC, \ "Enable the Java bytecode verifier for local classes") \ \ - develop(bool, VerifyStackAtCalls, false, \ - "Verify that the stack pointer is unchanged after calls") \ - \ develop(bool, VerifyCodeCache, false, \ "Verify code cache on memory allocation/deallocation") \ \ @@ -808,7 +805,7 @@ const int ObjectAlignmentInBytes = 8; \ product(bool, PrintValueLayout, false, DIAGNOSTIC, \ "Print field layout for each value class or class containing " \ - "inlined value fields") \ + "flattenable value fields") \ \ product(bool, PrintFlatArrayLayout, false, DIAGNOSTIC, \ "Print array layout for each flattened value array") \ @@ -818,7 +815,7 @@ const int ObjectAlignmentInBytes = 8; "when it determines it is possible and beneficial to do so") \ \ product(bool, UseFieldFlattening, true, DIAGNOSTIC, \ - "Allow the JVM to inline the fields of concrete value objects " \ + "Allow the JVM to flatten the fields of concrete value objects " \ "when it determines it is possible and beneficial to do so") \ \ product(bool, UseNullableAtomicValueFlattening, true, DIAGNOSTIC, \ diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 90b6a256715e..c2b9abd75544 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -1345,8 +1345,9 @@ void JavaThread::print_on(outputStream *st, bool print_extended_info) const { // print guess for valid stack memory region (assume 4K pages); helps lock debugging st->print_cr("[" INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12)); if (thread_oop != nullptr) { - if (is_vthread_mounted()) { - st->print_cr(" Carrying virtual thread #" INT64_FORMAT, java_lang_Thread::thread_id(vthread())); + oop vthread_oop = vthread(); + if (java_lang_VirtualThread::is_instance(vthread_oop)) { + st->print_cr(" Carrying virtual thread #" INT64_FORMAT, java_lang_Thread::thread_id(vthread_oop)); } else { st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop)); } diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index 9d0870e7d930..c28527e6a5cc 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -175,6 +175,9 @@ bool JavaThread::is_vthread_mounted() const { } const ContinuationEntry* JavaThread::vthread_continuation() const { + assert(is_handshake_safe_for(Thread::current()) || + SafepointSynchronize::is_at_safepoint() + JVMTI_ONLY(|| JavaThread::current()->is_vthread_transition_disabler()), ""); for (ContinuationEntry* c = last_continuation(); c != nullptr; c = c->parent()) { if (c->is_virtual_thread()) return c; diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp index e54e75688d68..cf615c1ccae3 100644 --- a/src/hotspot/share/runtime/jniHandles.cpp +++ b/src/hotspot/share/runtime/jniHandles.cpp @@ -68,7 +68,7 @@ jobject JNIHandles::make_local(JavaThread* thread, oop obj, AllocFailType alloc_ } else { assert(oopDesc::is_oop(obj), "not an oop"); assert(!current_thread_in_native(), "must not be in native"); - STATIC_ASSERT(TypeTag::local == 0); + static_assert(TypeTag::local == 0); return thread->active_handles()->allocate_handle(thread, obj, alloc_failmode); } } @@ -337,7 +337,7 @@ static inline uintptr_t untag_free_list(uintptr_t value) { // oops. The freelist handling currently relies on the size of oops // being the same as a native pointer. If this ever changes, then // this freelist handling must change too. -STATIC_ASSERT(sizeof(oop) == sizeof(uintptr_t)); +static_assert(sizeof(oop) == sizeof(uintptr_t)); #ifdef ASSERT void JNIHandleBlock::zap() { diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp index d29a4d1628b4..2c79619d68be 100644 --- a/src/hotspot/share/runtime/jniHandles.hpp +++ b/src/hotspot/share/runtime/jniHandles.hpp @@ -77,9 +77,9 @@ class JNIHandles : AllStatic { static const uintptr_t tag_size = 2; static const uintptr_t tag_mask = ((1u << tag_size) - 1u); - STATIC_ASSERT((TypeTag::local & tag_mask) == TypeTag::local); - STATIC_ASSERT((TypeTag::weak_global & tag_mask) == TypeTag::weak_global); - STATIC_ASSERT((TypeTag::global & tag_mask) == TypeTag::global); + static_assert((TypeTag::local & tag_mask) == TypeTag::local); + static_assert((TypeTag::weak_global & tag_mask) == TypeTag::weak_global); + static_assert((TypeTag::global & tag_mask) == TypeTag::global); // Resolve handle into oop inline static oop resolve(jobject handle); diff --git a/src/hotspot/share/runtime/jniHandles.inline.hpp b/src/hotspot/share/runtime/jniHandles.inline.hpp index 77c966d84d6d..d1f6a19e8e9d 100644 --- a/src/hotspot/share/runtime/jniHandles.inline.hpp +++ b/src/hotspot/share/runtime/jniHandles.inline.hpp @@ -50,7 +50,7 @@ inline bool JNIHandles::is_global_tagged(jobject handle) { inline oop* JNIHandles::local_ptr(jobject handle) { assert(is_local_tagged(handle), "precondition"); - STATIC_ASSERT(TypeTag::local == 0); + static_assert(TypeTag::local == 0); return reinterpret_cast(handle); } diff --git a/src/hotspot/share/runtime/lockStack.cpp b/src/hotspot/share/runtime/lockStack.cpp index a0f20b4fd315..36345ab9cc73 100644 --- a/src/hotspot/share/runtime/lockStack.cpp +++ b/src/hotspot/share/runtime/lockStack.cpp @@ -51,10 +51,10 @@ const int LockStack::lock_stack_base_offset = in_bytes(JavaThread::lock_stack_ba LockStack::LockStack(JavaThread* jt) : _top(lock_stack_base_offset), _base() { // Make sure the layout of the object is compatible with the emitted code's assumptions. - STATIC_ASSERT(sizeof(_bad_oop_sentinel) == oopSize); - STATIC_ASSERT(sizeof(_base[0]) == oopSize); - STATIC_ASSERT(std::is_standard_layout::value); - STATIC_ASSERT(offsetof(LockStack, _bad_oop_sentinel) == offsetof(LockStack, _base) - oopSize); + static_assert(sizeof(_bad_oop_sentinel) == oopSize); + static_assert(sizeof(_base[0]) == oopSize); + static_assert(std::is_standard_layout::value); + static_assert(offsetof(LockStack, _bad_oop_sentinel) == offsetof(LockStack, _base) - oopSize); #ifdef ASSERT for (int i = 0; i < CAPACITY; i++) { _base[i] = nullptr; @@ -116,5 +116,5 @@ void LockStack::print_on(outputStream* st) { } OMCache::OMCache(JavaThread* jt) { - STATIC_ASSERT(std::is_standard_layout::value); + static_assert(std::is_standard_layout::value); } diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp index 6627b4e4b6b8..bbaa3549fefb 100644 --- a/src/hotspot/share/runtime/mutex.cpp +++ b/src/hotspot/share/runtime/mutex.cpp @@ -294,7 +294,7 @@ Mutex::~Mutex() { Mutex::Mutex(Rank rank, const char * name, bool allow_vm_block) : _owner(nullptr) { assert(os::mutex_init_done(), "Too early!"); assert(name != nullptr, "Mutex requires a name"); - _name = os::strdup(name, mtInternal); + _name = os::strdup(name, mtSynchronizer); #ifdef ASSERT _allow_vm_block = allow_vm_block; _rank = rank; diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 00f0e4356b19..ce2a4b8fe246 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -299,7 +299,8 @@ ObjectMonitor::ObjectMonitor(oop object) : _unmounted_vthreads(0), _wait_set(nullptr), _waiters(0), - _wait_set_lock(0) + _wait_set_lock(0), + _object_strong_lock(0) { } ObjectMonitor::~ObjectMonitor() { diff --git a/src/hotspot/share/runtime/objectMonitorTable.cpp b/src/hotspot/share/runtime/objectMonitorTable.cpp index 44bc939240ab..5b7f13063135 100644 --- a/src/hotspot/share/runtime/objectMonitorTable.cpp +++ b/src/hotspot/share/runtime/objectMonitorTable.cpp @@ -665,8 +665,8 @@ ByteSize ObjectMonitorTable::table_capacity_mask_offset() { ByteSize ObjectMonitorTable::table_buckets_offset() { // Assumptions made from the emitted code about the layout. - STATIC_ASSERT(sizeof(Atomic) == sizeof(Entry*)); - STATIC_ASSERT(Atomic::value_offset_in_bytes() == 0); + static_assert(sizeof(Atomic) == sizeof(Entry*)); + static_assert(Atomic::value_offset_in_bytes() == 0); return byte_offset_of(Table, _buckets); } diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 9c77c5e30abf..63d9012b035b 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1341,6 +1341,20 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { // Compressed klass needs to be decoded first. // Todo: questionable for COH - can we do this better? #ifdef _LP64 + if (UseCompactObjectHeaders) { + markWord mw = (markWord)(uintptr_t)(addr); + static constexpr uintptr_t valhalla_reserved_bits_in_place = right_n_bits(markWord::valhalla_reserved_bits) + << markWord::valhalla_reserved_shift; + static constexpr uintptr_t markbits_must_be_zero = valhalla_reserved_bits_in_place | markWord::self_fwd_bit_in_place; + if ((!mw.has_hash()) && (mw.value() & markbits_must_be_zero) == 0 && Klass::is_valid(mw.klass_without_asserts())) { + st->print(PTR_FORMAT " looks like a valid markword: ", p2i(addr)); + mw.print_on(st); + st->print(" "); + mw.klass()->print_on(st); + return; + } + } + if (((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { narrowKlass narrow_klass = (narrowKlass)(uintptr_t)addr; Klass* k = CompressedKlassPointers::decode_without_asserts(narrow_klass); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 491d193eb95a..78bdcdfb2452 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -3024,7 +3024,7 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin bool has_scalarized_arguments = false; bool long_prev = false; int long_prev_offset = -1; - bool skipping_inline_recv = false; + bool skipping_value_recv = false; bool receiver_handled = false; fingerprint->iterate_args([&] (const AdapterFingerPrint::Element& arg) { @@ -3044,7 +3044,7 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin } assert(long_prev_offset != 0, "no buffer argument here"); SigEntry::add_entry(_sig_cc, bt_to_add, nullptr, long_prev_offset); - if (!skipping_inline_recv) { + if (!skipping_value_recv) { SigEntry::add_entry(_sig_cc_ro, bt_to_add, nullptr, long_prev_offset); } } @@ -3055,10 +3055,10 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin assert(ValueTypePassFieldsAsArgs, "unexpected end of value type"); value_object_count--; SigEntry::add_entry(_sig_cc, T_VOID, nullptr, offset); - if (!skipping_inline_recv) { + if (!skipping_value_recv) { SigEntry::add_entry(_sig_cc_ro, T_VOID, nullptr, offset); } else if (value_object_count == 0) { - skipping_inline_recv = false; + skipping_value_recv = false; } assert(value_object_count >= 0, "invalid value object count"); } else { @@ -3072,7 +3072,7 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin SigEntry::add_entry(_sig, bt); } SigEntry::add_entry(_sig_cc, bt, nullptr, offset); - if (!skipping_inline_recv) { + if (!skipping_value_recv) { SigEntry::add_entry(_sig_cc_ro, bt, nullptr, offset); } break; @@ -3089,7 +3089,7 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin assert(value_object_count > 0, "must be value object field"); assert(offset != 0 || (bt == T_OBJECT && prev_bt == T_METADATA), "buffer input expected here"); SigEntry::add_entry(_sig_cc, bt, nullptr, offset, offset == -1, offset == 0); - if (!skipping_inline_recv) { + if (!skipping_value_recv) { SigEntry::add_entry(_sig_cc_ro, bt, nullptr, offset, offset == -1, offset == 0); } break; @@ -3099,10 +3099,10 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin SigEntry::add_entry(_sig, T_OBJECT); } SigEntry::add_entry(_sig_cc, T_METADATA, nullptr, offset); - if (!skipping_inline_recv) { + if (!skipping_value_recv) { if (!receiver_handled && _has_value_recv && value_object_count == 0) { SigEntry::add_entry(_sig_cc_ro, T_OBJECT); - skipping_inline_recv = true; + skipping_value_recv = true; receiver_handled = true; } else { SigEntry::add_entry(_sig_cc_ro, T_METADATA, nullptr, offset); @@ -3128,10 +3128,10 @@ void CompiledEntrySignature::initialize_from_fingerprint(AdapterFingerPrint* fin #ifdef ASSERT if (_has_value_recv) { - // In RO signatures, inline receivers must be represented as a single T_OBJECT + // In RO signatures, value receivers must be represented as a single T_OBJECT assert(_sig_cc_ro->length() >= 1, "sig_cc_ro must include receiver"); assert(_sig_cc_ro->at(0)._bt == T_OBJECT, - "sig_cc_ro must represent inline receiver as T_OBJECT"); + "sig_cc_ro must represent value receiver as T_OBJECT"); assert(_sig_cc_ro->length() <= _sig_cc->length(), "sig_cc_ro must not be longer than sig_cc"); } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index f1ffe48be88e..523fb402413a 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -65,7 +65,6 @@ #include "utilities/events.hpp" #include "utilities/globalCounter.inline.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/linkedlist.hpp" #include "utilities/preserveException.hpp" class ObjectMonitorDeflationLogging; diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 6de56c30d5fe..4613d4c33d10 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -494,7 +494,7 @@ void Thread::print_on(outputStream* st, bool print_extended_info) const { } st->print("tid=" INTPTR_FORMAT " ", p2i(this)); - if (!is_Java_thread() || !JavaThread::cast(this)->is_vthread_mounted()) { + if (!is_Java_thread() || !java_lang_VirtualThread::is_instance(JavaThread::cast(this)->vthread())) { osthread()->print_on(st); } } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 5b0f53f2189c..29c12c3c558e 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -152,7 +152,7 @@ class Thread: public ThreadShadow { } template T* gc_data() { - STATIC_ASSERT(sizeof(T) <= sizeof(_gc_data)); + static_assert(sizeof(T) <= sizeof(_gc_data)); return reinterpret_cast(&_gc_data); } diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index 447c2a84ef24..c72ec5d3834a 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -89,6 +89,7 @@ template(ShenandoahFullGC) \ template(ShenandoahInitMark) \ template(ShenandoahFinalMarkStartEvac) \ + template(ShenandoahFinalRoots) \ template(ShenandoahInitUpdateRefs) \ template(ShenandoahFinalUpdateRefs) \ template(ShenandoahFinalVerify) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 00974a1a1940..695e073bf768 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -191,6 +191,7 @@ nonstatic_field(ResolvedMethodEntry, _cpool_index, u2) \ nonstatic_field(ConstantPoolCache, _resolved_indy_entries, Array*) \ nonstatic_field(ResolvedIndyEntry, _cpool_index, u2) \ + nonstatic_field(ValueFieldLayoutInfo, _klass, ValueKlass*) \ volatile_nonstatic_field(InstanceKlass, _array_klasses, ObjArrayKlass*) \ nonstatic_field(InstanceKlass, _methods, Array*) \ nonstatic_field(InstanceKlass, _default_methods, Array*) \ @@ -220,6 +221,10 @@ nonstatic_field(InstanceKlass, _method_ordering, Array*) \ nonstatic_field(InstanceKlass, _default_vtable_indices, Array*) \ nonstatic_field(InstanceKlass, _access_flags, AccessFlags) \ + nonstatic_field(InstanceKlass, _value_field_layout_info_array, Array*) \ + nonstatic_field(InstanceKlass, _adr_value_klass_members, address) \ + nonstatic_field(ValueKlass::Members, _payload_offset, int) \ + nonstatic_field(ValueKlass::Members, _null_marker_offset, int) \ nonstatic_field(Klass, _kind, const Klass::KlassKind) \ nonstatic_field(Klass, _super_check_offset, juint) \ nonstatic_field(Klass, _secondary_super_cache, Klass*) \ @@ -729,6 +734,7 @@ unchecked_nonstatic_field(Array,_data, sizeof(ResolvedMethodEntry)) \ unchecked_nonstatic_field(Array, _data, sizeof(ResolvedIndyEntry)) \ unchecked_nonstatic_field(Array*>, _data, sizeof(Array*)) \ + unchecked_nonstatic_field(Array,_data, sizeof(ValueFieldLayoutInfo)) \ \ /*********************************/ \ /* java_lang_Class fields */ \ @@ -947,6 +953,9 @@ declare_type(MethodCounters, MetaspaceObj) \ declare_type(ConstMethod, MetaspaceObj) \ declare_type(Annotations, MetaspaceObj) \ + declare_type(ValueFieldLayoutInfo, MetaspaceObj) \ + \ + declare_toplevel_type(ValueKlass::Members) \ \ declare_toplevel_type(narrowKlass) \ \ @@ -1207,6 +1216,7 @@ declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ declare_type(Array*>, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ \ declare_toplevel_type(BitMap) \ declare_type(BitMapView, BitMap) \ diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 37a672556a29..2d195a818746 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -916,10 +916,9 @@ void ThreadSnapshot::initialize(ThreadsList * t_list, JavaThread* thread) { oop blocker_object = nullptr; oop blocker_object_owner = nullptr; - if (thread->is_vthread_mounted() && thread->vthread() != threadObj) { // ThreadSnapshot only captures platform threads + oop vthread = thread->vthread(); + if (java_lang_VirtualThread::is_instance(vthread)) { // ThreadSnapshot only captures platform threads _thread_status = JavaThreadStatus::IN_OBJECT_WAIT; - oop vthread = thread->vthread(); - assert(vthread != nullptr, ""); blocker_object = vthread; blocker_object_owner = vthread; } else if (_thread_status == JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER || diff --git a/src/hotspot/share/utilities/align.hpp b/src/hotspot/share/utilities/align.hpp index dcd58477800f..0c2062153d8c 100644 --- a/src/hotspot/share/utilities/align.hpp +++ b/src/hotspot/share/utilities/align.hpp @@ -94,7 +94,7 @@ constexpr T align_down_bounded(T size, A alignment) { template inline bool can_align_up(void* ptr, A alignment) { - static_assert(sizeof(ptr) == sizeof(uintptr_t), "assumption"); + static_assert(sizeof(ptr) == sizeof(uintptr_t)); return can_align_up((uintptr_t)ptr, alignment); } diff --git a/src/hotspot/share/utilities/bitMap.cpp b/src/hotspot/share/utilities/bitMap.cpp index 7fc280f2de3e..c07d174c1730 100644 --- a/src/hotspot/share/utilities/bitMap.cpp +++ b/src/hotspot/share/utilities/bitMap.cpp @@ -33,7 +33,7 @@ using bm_word_t = BitMap::bm_word_t; using idx_t = BitMap::idx_t; -STATIC_ASSERT(sizeof(bm_word_t) == BytesPerWord); // "Implementation assumption." +static_assert(sizeof(bm_word_t) == BytesPerWord); // "Implementation assumption." // For the BitMaps with allocators that don't support reallocate template @@ -306,7 +306,7 @@ bool BitMap::is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word) { // because beg_full_word > end_full_word can occur when beg and end are in // the same word. // The threshold should be at least one word. - STATIC_ASSERT(small_range_words >= 1); + static_assert(small_range_words >= 1); return beg_full_word + small_range_words >= end_full_word; } diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 1aae97abdff3..56d021e246f8 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -54,7 +54,7 @@ class BitMap { typedef uintptr_t bm_word_t; // Element type of array that represents the // bitmap, with BitsPerWord bits per element. // If this were to fail, there are lots of places that would need repair. - STATIC_ASSERT((sizeof(bm_word_t) * BitsPerByte) == BitsPerWord); + static_assert((sizeof(bm_word_t) * BitsPerByte) == BitsPerWord); // Hints for range sizes. typedef enum { diff --git a/src/hotspot/share/utilities/bitMap.inline.hpp b/src/hotspot/share/utilities/bitMap.inline.hpp index 833b89c75078..f086ae2164a6 100644 --- a/src/hotspot/share/utilities/bitMap.inline.hpp +++ b/src/hotspot/share/utilities/bitMap.inline.hpp @@ -196,7 +196,7 @@ inline void BitMap::par_clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { template inline BitMap::idx_t BitMap::find_first_bit_impl(idx_t beg, idx_t end) const { - STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); + static_assert(flip == find_ones_flip || flip == find_zeros_flip); verify_range(beg, end); assert(!aligned_right || is_aligned(end, BitsPerWord), "end not aligned"); @@ -240,7 +240,7 @@ inline BitMap::idx_t BitMap::find_first_bit_impl(idx_t beg, idx_t end) const { template inline BitMap::idx_t BitMap::find_last_bit_impl(idx_t beg, idx_t end) const { - STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); + static_assert(flip == find_ones_flip || flip == find_zeros_flip); verify_range(beg, end); assert(!aligned_left || is_aligned(beg, BitsPerWord), "beg not aligned"); diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 831a3c4e6bcf..10c6dd059642 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -779,34 +779,6 @@ extern "C" bool dbg_is_good_oop(oopDesc* o) { return dbg_is_safe(o, -1) && dbg_is_safe(o->klass(), -1) && oopDesc::is_oop(o) && o->klass()->is_klass(); } -////////////////////////////////////////////////////////////////////////////// -// Test multiple STATIC_ASSERT forms in various scopes. - -#ifndef PRODUCT - -// namespace scope -STATIC_ASSERT(true); -STATIC_ASSERT(true); -STATIC_ASSERT(1 == 1); -STATIC_ASSERT(0 == 0); - -void test_multiple_static_assert_forms_in_function_scope() { - STATIC_ASSERT(true); - STATIC_ASSERT(true); - STATIC_ASSERT(0 == 0); - STATIC_ASSERT(1 == 1); -} - -// class scope -struct TestMultipleStaticAssertFormsInClassScope { - STATIC_ASSERT(true); - STATIC_ASSERT(true); - STATIC_ASSERT(0 == 0); - STATIC_ASSERT(1 == 1); -}; - -#endif // !PRODUCT - // Support for showing register content on asserts/guarantees. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT void initialize_assert_poison() { diff --git a/src/hotspot/share/utilities/debug.hpp b/src/hotspot/share/utilities/debug.hpp index 127241536598..bf2b09bea05b 100644 --- a/src/hotspot/share/utilities/debug.hpp +++ b/src/hotspot/share/utilities/debug.hpp @@ -288,8 +288,6 @@ void report_untested(const char* file, int line, const char* message); ATTRIBUTE_PRINTF(1, 2) void warning(const char* format, ...); -#define STATIC_ASSERT(Cond) static_assert((Cond), #Cond) - // out of memory reporting void report_java_out_of_memory(const char* message); diff --git a/src/hotspot/share/utilities/devirtualizer.inline.hpp b/src/hotspot/share/utilities/devirtualizer.inline.hpp index 8cc6f9319081..10d6e2de2cbd 100644 --- a/src/hotspot/share/utilities/devirtualizer.inline.hpp +++ b/src/hotspot/share/utilities/devirtualizer.inline.hpp @@ -85,7 +85,7 @@ ALWAYSINLINE static typename EnableIf::value, void>::type call_do_oop(void (Receiver::*)(T*), void (Base::*)(T*), OopClosureType* closure, T* p) { // Sanity check - STATIC_ASSERT((!std::is_same::value)); + static_assert(!std::is_same::value); closure->OopClosureType::do_oop(p); } diff --git a/src/hotspot/share/utilities/events.hpp b/src/hotspot/share/utilities/events.hpp index 0d1c08548ff7..766789d2dbb0 100644 --- a/src/hotspot/share/utilities/events.hpp +++ b/src/hotspot/share/utilities/events.hpp @@ -48,7 +48,7 @@ // crash time. This is a very generic interface that is mainly here // for completeness. Normally the templated EventLogBase would be // subclassed to provide different log types. -class EventLog : public CHeapObj { +class EventLog : public CHeapObj { friend class Events; private: @@ -79,7 +79,7 @@ class EventLog : public CHeapObj { // semantics aren't appropriate. The name is used as the label of the // log when it is dumped during a crash. template class EventLogBase : public EventLog { - template class EventRecord : public CHeapObj { + template class EventRecord : public CHeapObj { public: double timestamp; Thread* thread; diff --git a/src/hotspot/share/utilities/globalDefinitions.cpp b/src/hotspot/share/utilities/globalDefinitions.cpp index 0765f781ada7..a7822fa1ffd9 100644 --- a/src/hotspot/share/utilities/globalDefinitions.cpp +++ b/src/hotspot/share/utilities/globalDefinitions.cpp @@ -404,11 +404,11 @@ size_t lcm(size_t a, size_t b) { // Test that nth_bit macro and friends behave as // expected, even with low-precedence operators. -STATIC_ASSERT(nth_bit(3) == 0x8); -STATIC_ASSERT(nth_bit(1|2) == 0x8); +static_assert(nth_bit(3) == 0x8); +static_assert(nth_bit(1|2) == 0x8); -STATIC_ASSERT(right_n_bits(3) == 0x7); -STATIC_ASSERT(right_n_bits(1|2) == 0x7); +static_assert(right_n_bits(3) == 0x7); +static_assert(right_n_bits(1|2) == 0x7); // Check for Flush-To-Zero mode diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 067d3e5ed1bc..aedccc3cfd1c 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1245,7 +1245,7 @@ inline jlong java_negate(jlong v) { return java_subtract((jlong)0, v); } #define JAVA_INTEGER_SHIFT_OP(OP, NAME, TYPE, XTYPE) \ inline TYPE NAME (TYPE lhs, jint rhs) { \ const uint rhs_mask = (sizeof(TYPE) * 8) - 1; \ - STATIC_ASSERT(rhs_mask == 31 || rhs_mask == 63); \ + static_assert(rhs_mask == 31 || rhs_mask == 63); \ XTYPE xres = static_cast(lhs); \ xres OP ## = (rhs & rhs_mask); \ return reinterpret_cast(xres); \ diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 13ac1ed360f3..e2a1cfe64baf 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -837,7 +837,7 @@ template class GrowableArrayCHeap : public GrowableArrayWithAllocator > { friend class GrowableArrayWithAllocator >; - STATIC_ASSERT(MT != mtNone); + static_assert(MT != mtNone); static E* allocate(int max, MemTag mem_tag) { return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp index 1b8e7de652e8..dfdd6a099189 100644 --- a/src/hotspot/share/utilities/intn_t.hpp +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -76,7 +76,7 @@ class intn_t { constexpr static int min = std::numeric_limits::max() << (nbits - 1); constexpr static int max = (1 << (nbits - 1)) - 1; - static_assert(min < max, ""); + static_assert(min < max); constexpr bool operator==(intn_t o) const { return (_v & _mask) == (o._v & _mask); } constexpr bool operator!=(intn_t o) const { return !(*this == o); } @@ -118,7 +118,7 @@ class uintn_t { constexpr static int min = 0; constexpr static int max = _mask; - static_assert(min < max, ""); + static_assert(min < max); constexpr bool operator==(uintn_t o) const { return (_v & _mask) == (o._v & _mask); } constexpr bool operator!=(uintn_t o) const { return (_v & _mask) != (o._v & _mask); } diff --git a/src/hotspot/share/utilities/linkedlist.hpp b/src/hotspot/share/utilities/linkedlist.hpp deleted file mode 100644 index f0802b754ff7..000000000000 --- a/src/hotspot/share/utilities/linkedlist.hpp +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_UTILITIES_LINKEDLIST_HPP -#define SHARE_UTILITIES_LINKEDLIST_HPP - -#include "memory/allocation.hpp" - -/* - * The implementation of a generic linked list, which uses various - * backing storages, such as C heap, arena and resource, etc. - */ - - -// An entry in a linked list. It should use the same backing storage -// as the linked list that contains this entry. -template class LinkedListNode : public AnyObj { - private: - E _data; // embedded content - LinkedListNode* _next; // next entry - - // Select member function 'bool U::equals(const U&) const' if 'U' is of class - // type. This works because of the "Substitution Failure Is Not An Error" - // (SFINAE) rule. Notice that this version of 'equal' will also be chosen for - // class types which don't define a corresponding 'equals()' method (and will - // result in a compilation error for them). It is not easily possible to - // specialize this 'equal()' function exclusively for class types which define - // the correct 'equals()' function because that function can be in a base - // class, a dependent base class or have a compatible but slightly different - // signature. - template - static bool equal(const U& a, const U& b, bool (U::*t)(const U&) const) { - return a.equals(b); - } - - template - static bool equal(const U& a, const U& b, ...) { - return a == b; - } - - protected: - LinkedListNode() : _next(nullptr) { } - - public: - LinkedListNode(const E& e): _data(e), _next(nullptr) { } - - inline void set_next(LinkedListNode* node) { _next = node; } - inline LinkedListNode * next() const { return _next; } - - E* data() { return &_data; } - const E* peek() const { return &_data; } - - bool equals(const E& t) const { - return equal(_data, t, nullptr); - } -}; - -// A linked list interface. It does not specify -// any storage type it uses, so all methods involving -// memory allocation or deallocation are pure virtual -template class LinkedList : public AnyObj { - protected: - LinkedListNode* _head; - NONCOPYABLE(LinkedList); - - public: - LinkedList() : _head(nullptr) { } - virtual ~LinkedList() {} - - inline void set_head(LinkedListNode* h) { _head = h; } - inline LinkedListNode* head() const { return _head; } - inline bool is_empty() const { return head() == nullptr; } - - inline size_t size() const { - LinkedListNode* p; - size_t count = 0; - for (p = head(); p != nullptr; count++, p = p->next()); - return count; - } - - // Move all entries from specified linked list to this one - virtual void move(LinkedList* list) = 0; - - // Add an entry to this linked list - virtual LinkedListNode* add(const E& e) = 0; - // Add all entries from specified linked list to this one, - virtual void add(LinkedListNode* node) = 0; - - // Add a linked list to this linked list - virtual bool add(const LinkedList* list) = 0; - - // Search entry in the linked list - virtual LinkedListNode* find_node(const E& e) = 0; - virtual E* find(const E& e) = 0; - - // Insert entry to the linked list - virtual LinkedListNode* insert_before(const E& e, LinkedListNode* ref) = 0; - virtual LinkedListNode* insert_after (const E& e, LinkedListNode* ref) = 0; - - // Remove entry from the linked list - virtual bool remove(const E& e) = 0; - virtual bool remove(LinkedListNode* node) = 0; - virtual bool remove_before(LinkedListNode* ref) = 0; - virtual bool remove_after(LinkedListNode* ref) = 0; - - LinkedListNode* unlink_head() { - LinkedListNode* h = this->head(); - if (h != nullptr) { - this->set_head(h->next()); - } - return h; - } - - DEBUG_ONLY(virtual AnyObj::allocation_type storage_type() = 0;) -}; - -// A linked list implementation. -// The linked list can be allocated in various type of memory: C heap, arena and resource area, etc. -template - class LinkedListImpl : public LinkedList { - protected: - Arena* _arena; - public: - LinkedListImpl() : _arena(nullptr) { } - LinkedListImpl(Arena* a) : _arena(a) { } - - virtual ~LinkedListImpl() { - clear(); - } - - virtual void clear() { - LinkedListNode* p = this->head(); - this->set_head(nullptr); - while (p != nullptr) { - LinkedListNode* to_delete = p; - p = p->next(); - delete_node(to_delete); - } - } - - // Add an entry to the linked list - virtual LinkedListNode* add(const E& e) { - LinkedListNode* node = this->new_node(e); - if (node != nullptr) { - this->add(node); - } - - return node; - } - - virtual void add(LinkedListNode* node) { - assert(node != nullptr, "null pointer"); - node->set_next(this->head()); - this->set_head(node); - } - - // Move a linked list to this linked list, both have to be allocated on the same - // storage type. - virtual void move(LinkedList* list) { - assert(list->storage_type() == this->storage_type(), "Different storage type"); - LinkedListNode* node = this->head(); - while (node != nullptr && node->next() != nullptr) { - node = node->next(); - } - if (node == nullptr) { - this->set_head(list->head()); - } else { - node->set_next(list->head()); - } - // All entries are moved - list->set_head(nullptr); - } - - virtual bool add(const LinkedList* list) { - LinkedListNode* node = list->head(); - while (node != nullptr) { - if (this->add(*node->peek()) == nullptr) { - return false; - } - node = node->next(); - } - return true; - } - - - virtual LinkedListNode* find_node(const E& e) { - LinkedListNode* p = this->head(); - while (p != nullptr && !p->equals(e)) { - p = p->next(); - } - return p; - } - - E* find(const E& e) { - LinkedListNode* node = find_node(e); - return (node == nullptr) ? nullptr : node->data(); - } - - - // Add an entry in front of the reference entry - LinkedListNode* insert_before(const E& e, LinkedListNode* ref_node) { - LinkedListNode* node = this->new_node(e); - if (node == nullptr) return nullptr; - if (ref_node == this->head()) { - node->set_next(ref_node); - this->set_head(node); - } else { - LinkedListNode* p = this->head(); - while (p != nullptr && p->next() != ref_node) { - p = p->next(); - } - assert(p != nullptr, "ref_node not in the list"); - node->set_next(ref_node); - p->set_next(node); - } - return node; - } - - // Add an entry behind the reference entry - LinkedListNode* insert_after(const E& e, LinkedListNode* ref_node) { - LinkedListNode* node = this->new_node(e); - if (node == nullptr) return nullptr; - node->set_next(ref_node->next()); - ref_node->set_next(node); - return node; - } - - // Remove an entry from the linked list. - // Return true if the entry is successfully removed - virtual bool remove(const E& e) { - LinkedListNode* tmp = this->head(); - LinkedListNode* prev = nullptr; - - while (tmp != nullptr) { - if (tmp->equals(e)) { - return remove_after(prev); - } - prev = tmp; - tmp = tmp->next(); - } - return false; - } - - // Remove the node after the reference entry - virtual bool remove_after(LinkedListNode* prev) { - LinkedListNode* to_delete; - if (prev == nullptr) { - to_delete = this->unlink_head(); - } else { - to_delete = prev->next(); - if (to_delete != nullptr) { - prev->set_next(to_delete->next()); - } - } - - if (to_delete != nullptr) { - delete_node(to_delete); - return true; - } - return false; - } - - virtual bool remove(LinkedListNode* node) { - LinkedListNode* p = this->head(); - if (p == node) { - this->set_head(p->next()); - delete_node(node); - return true; - } - while (p != nullptr && p->next() != node) { - p = p->next(); - } - if (p != nullptr) { - p->set_next(node->next()); - delete_node(node); - return true; - } else { - return false; - } - } - - virtual bool remove_before(LinkedListNode* ref) { - assert(ref != nullptr, "null pointer"); - LinkedListNode* p = this->head(); - LinkedListNode* to_delete = nullptr; // to be deleted - LinkedListNode* prev = nullptr; // node before the node to be deleted - while (p != nullptr && p != ref) { - prev = to_delete; - to_delete = p; - p = p->next(); - } - if (p == nullptr || to_delete == nullptr) return false; - assert(to_delete->next() == ref, "Wrong node to delete"); - assert(prev == nullptr || prev->next() == to_delete, - "Sanity check"); - if (prev == nullptr) { - assert(to_delete == this->head(), "Must be head"); - this->set_head(to_delete->next()); - } else { - prev->set_next(to_delete->next()); - } - delete_node(to_delete); - return true; - } - - DEBUG_ONLY(AnyObj::allocation_type storage_type() { return T; }) - protected: - // Create new linked list node object in specified storage - LinkedListNode* new_node(const E& e) const { - switch(T) { - case AnyObj::ARENA: { - assert(_arena != nullptr, "Arena not set"); - return new(_arena) LinkedListNode(e); - } - case AnyObj::RESOURCE_AREA: - if (alloc_failmode == AllocFailStrategy::RETURN_NULL) { - return new(std::nothrow) LinkedListNode(e); - } else { - return new LinkedListNode(e); - } - case AnyObj::C_HEAP: { - if (alloc_failmode == AllocFailStrategy::RETURN_NULL) { - return new(std::nothrow, MT) LinkedListNode(e); - } else { - return new(MT) LinkedListNode(e); - } - } - default: - ShouldNotReachHere(); - } - return nullptr; - } - - // Delete linked list node object - void delete_node(LinkedListNode* node) { - if (T == AnyObj::C_HEAP) { - delete node; - } - } -}; - -// Sorted linked list. The linked list maintains sorting order specified by the comparison -// function -template - class SortedLinkedList : public LinkedListImpl { - public: - SortedLinkedList() { } - SortedLinkedList(Arena* a) : LinkedListImpl(a) { } - - virtual LinkedListNode* add(const E& e) { - return LinkedListImpl::add(e); - } - - virtual void move(LinkedList* list) { - assert(list->storage_type() == this->storage_type(), "Different storage type"); - LinkedListNode* node; - while ((node = list->unlink_head()) != nullptr) { - this->add(node); - } - assert(list->is_empty(), "All entries are moved"); - } - - virtual void add(LinkedListNode* node) { - assert(node != nullptr, "null pointer"); - LinkedListNode* tmp = this->head(); - LinkedListNode* prev = nullptr; - - int cmp_val; - while (tmp != nullptr) { - cmp_val = FUNC(*tmp->peek(), *node->peek()); - if (cmp_val >= 0) { - break; - } - prev = tmp; - tmp = tmp->next(); - } - - if (prev != nullptr) { - node->set_next(prev->next()); - prev->set_next(node); - } else { - node->set_next(this->head()); - this->set_head(node); - } - } - - virtual bool add(const LinkedList* list) { - return LinkedListImpl::add(list); - } - - virtual LinkedListNode* find_node(const E& e) { - LinkedListNode* p = this->head(); - - while (p != nullptr) { - int comp_val = FUNC(*p->peek(), e); - if (comp_val == 0) { - return p; - } else if (comp_val > 0) { - return nullptr; - } - p = p->next(); - } - return nullptr; - } -}; - -// Iterates all entries in the list -template class LinkedListIterator : public StackObj { - private: - mutable LinkedListNode* _p; - - public: - LinkedListIterator(LinkedListNode* head) : _p(head) {} - - bool is_empty() const { return _p == nullptr; } - - E* next() { - if (_p == nullptr) return nullptr; - E* e = _p->data(); - _p = _p->next(); - return e; - } - - const E* next() const { - if (_p == nullptr) return nullptr; - const E* e = _p->peek(); - _p = _p->next(); - return e; - } -}; - -#endif // SHARE_UTILITIES_LINKEDLIST_HPP diff --git a/src/hotspot/share/utilities/parseInteger.hpp b/src/hotspot/share/utilities/parseInteger.hpp index 30e0d45d1cfb..dbc339341ae7 100644 --- a/src/hotspot/share/utilities/parseInteger.hpp +++ b/src/hotspot/share/utilities/parseInteger.hpp @@ -46,7 +46,7 @@ template ::value), ENABLE_IF(sizeof(T) = inline bool parse_integer_impl(const char *s, char **endptr, int base, T* result) { // Don't use strtol -- on 64-bit builds, "long" could be either 32- or 64-bits // so the range tests could be tautological and might cause compiler warnings. - STATIC_ASSERT(sizeof(long long) >= 8); // C++ specification + static_assert(sizeof(long long) >= 8); // C++ specification errno = 0; // errno is thread safe long long v = strtoll(s, endptr, base); if (errno != 0 || v < min_jint || v > max_jint) { @@ -62,7 +62,7 @@ inline bool parse_integer_impl(const char *s, char **endptr, int base, T* result return false; } // Don't use strtoul -- same reason as above. - STATIC_ASSERT(sizeof(unsigned long long) >= 8); // C++ specification + static_assert(sizeof(unsigned long long) >= 8); // C++ specification errno = 0; // errno is thread safe unsigned long long v = strtoull(s, endptr, base); if (errno != 0 || v > max_juint) { diff --git a/src/hotspot/share/utilities/population_count.hpp b/src/hotspot/share/utilities/population_count.hpp index 9d9c07456007..3ad13297149f 100644 --- a/src/hotspot/share/utilities/population_count.hpp +++ b/src/hotspot/share/utilities/population_count.hpp @@ -44,10 +44,10 @@ // a 32-bit integer type. template constexpr unsigned population_count(T x) { - STATIC_ASSERT(BitsPerWord <= 128); - STATIC_ASSERT(BitsPerByte == 8); - STATIC_ASSERT(std::is_integral::value); - STATIC_ASSERT(!std::is_signed::value); + static_assert(BitsPerWord <= 128); + static_assert(BitsPerByte == 8); + static_assert(std::is_integral::value); + static_assert(!std::is_signed::value); // We need to take care with implicit integer promotion when dealing with // integers < 32-bit. We chose to do this by explicitly widening constants // to unsigned diff --git a/src/hotspot/share/utilities/unsigned5.hpp b/src/hotspot/share/utilities/unsigned5.hpp index 1fc6aeaffb60..bbf290e15e0d 100644 --- a/src/hotspot/share/utilities/unsigned5.hpp +++ b/src/hotspot/share/utilities/unsigned5.hpp @@ -114,7 +114,7 @@ class UNSIGNED5 : AllStatic { template> static uint32_t read_uint(ARR array, OFF& offset_rw, OFF limit, GET get = GET()) { const OFF pos = offset_rw; - STATIC_ASSERT(sizeof(get(array, pos)) == 1); // must be a byte-getter + static_assert(sizeof(get(array, pos)) == 1); // must be a byte-getter const uint32_t b_0 = (uint8_t) get(array, pos); //b_0 = a[0] assert(b_0 >= X, "avoid excluded bytes"); uint32_t sum = b_0 - X; @@ -217,7 +217,7 @@ class UNSIGNED5 : AllStatic { static int check_length(ARR array, OFF offset, OFF limit = 0, GET get = GET()) { const OFF pos = offset; - STATIC_ASSERT(sizeof(get(array, pos)) == 1); // must be a byte-getter + static_assert(sizeof(get(array, pos)) == 1); // must be a byte-getter const uint32_t b_0 = (uint8_t) get(array, pos); //b_0 = a[0] if (b_0 < X+L) { return (b_0 < X) ? 0 : 1; diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index ad8dc5b8bf2b..34bf43ecc036 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -29,6 +29,7 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.MGF1ParameterSpec; +import java.security.spec.NamedParameterSpec; import java.security.spec.PSSParameterSpec; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; @@ -44,6 +45,19 @@ import sun.security.util.SignatureUtil; enum SignatureScheme { + // ML_DSA algorithms + MLDSA65 (0x0905, "mldsa65", "ML-DSA-65", + "ML-DSA", + ProtocolVersion.PROTOCOLS_OF_13), + + MLDSA87 (0x0906, "mldsa87", "ML-DSA-87", + "ML-DSA", + ProtocolVersion.PROTOCOLS_OF_13), + + MLDSA44 (0x0904, "mldsa44", "ML-DSA-44", + "ML-DSA", + ProtocolVersion.PROTOCOLS_OF_13), + // ECDSA algorithms ECDSA_SECP256R1_SHA256 (0x0403, "ecdsa_secp256r1_sha256", "SHA256withECDSA", @@ -537,6 +551,19 @@ static Map.Entry getSignerOfPreferableAlgorithm( if (keySize >= ss.minimalKeySize && keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) && ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) { + if (signingKey.getParams() instanceof NamedParameterSpec nps + && !ss.algorithm.equalsIgnoreCase(nps.getName())) { + if (SSLLogger.isOn() && + SSLLogger.isOn( + SSLLogger.Opt.HANDSHAKE_VERBOSE)) { + SSLLogger.finest( + "Ignore the signature algorithm (" + ss + + "), unsupported named parameter: " + + nps.getName()); + } + continue; + } + if ((ss.namedGroup != null) && (ss.namedGroup.spec == NamedGroupSpec.NAMED_GROUP_ECDHE)) { ECParameterSpec params = @@ -642,6 +669,12 @@ Signature getVerifier(PublicKey publicKey) throws NoSuchAlgorithmException, return null; } + if (publicKey.getParams() instanceof NamedParameterSpec nps + && !algorithm.equalsIgnoreCase(nps.getName())) { + throw new InvalidKeyException("Unsupported named parameter: " + + nps.getName()); + } + Signature verifier = Signature.getInstance(algorithm); SignatureUtil.initVerifyWithParam(verifier, publicKey, (signAlgParams != null ? signAlgParams.parameterSpec : null)); diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 1c68c13bbdca..893067d4dc97 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java @@ -261,7 +261,7 @@ boolean isPostHandshakeContext() { (handshakeContext instanceof PostHandshakeContext); } - // Note: Don't use this method for close_nofity, use closeNotify() instead. + // Note: Don't use this method for close_notify, use closeNotify() instead. void warning(Alert alert) { // For initial handshaking, don't send a warning alert message to peer // if handshaker has not started. diff --git a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java index bdb2dd7706f2..3a3527483541 100644 --- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,9 @@ enum X509Authentication implements SSLAuthentication { // Require EC public key EC ("EC", "EC"), // Edwards-Curve key - EDDSA ("EdDSA", "EdDSA"); + EDDSA ("EdDSA", "EdDSA"), + // Require ML-DSA public key + MLDSA ("ML-DSA", "ML-DSA"); final String keyAlgorithm; final String[] keyTypes; diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index f7b690f10324..9bc1b1f899a9 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -94,20 +94,10 @@ jint IPv4_supported() return JNI_TRUE; } -#if defined(DONT_ENABLE_IPV6) -jint IPv6_supported() -{ - return JNI_FALSE; -} - -#else /* !DONT_ENABLE_IPV6 */ - jint IPv6_supported() { int fd; void *ipv6_fn; - SOCKETADDRESS sa; - socklen_t sa_len = sizeof(SOCKETADDRESS); fd = socket(AF_INET6, SOCK_STREAM, 0) ; if (fd < 0) { @@ -151,7 +141,6 @@ jint IPv6_supported() return JNI_TRUE; } } -#endif /* DONT_ENABLE_IPV6 */ jint reuseport_supported(int ipv6_available) { diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix/native/libnio/ch/Net.c index ad0994aa3e72..fd93f7c28967 100644 --- a/src/java.base/unix/native/libnio/ch/Net.c +++ b/src/java.base/unix/native/libnio/ch/Net.c @@ -101,32 +101,6 @@ static jboolean isIPv4MappedGroup(struct group_source_req *req) { return IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) ? JNI_TRUE : JNI_FALSE; } -/* - * Checks whether or not "socket extensions for multicast source filters" is supported. - * Returns JNI_TRUE if it is supported, JNI_FALSE otherwise - */ -static jboolean isSourceFilterSupported(){ - static jboolean alreadyChecked = JNI_FALSE; - static jboolean result = JNI_TRUE; - if (alreadyChecked != JNI_TRUE){ - struct utsname uts; - memset(&uts, 0, sizeof(uts)); - strcpy(uts.sysname, "?"); - const int utsRes = uname(&uts); - int major = -1; - int minor = -1; - major = atoi(uts.version); - minor = atoi(uts.release); - if (strcmp(uts.sysname, "AIX") == 0) { - if (major < 6 || (major == 6 && minor < 1)) {// unsupported on aix < 6.1 - result = JNI_FALSE; - } - } - alreadyChecked = JNI_TRUE; - } - return result; -} - #endif /* _AIX */ static jclass isa_class; /* java.net.InetSocketAddress */ @@ -651,14 +625,6 @@ Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobjec optval = (void*)&mreq; optlen = sizeof(mreq); } else { - -#ifdef _AIX - /* check AIX for support of source filtering */ - if (isSourceFilterSupported() != JNI_TRUE){ - return IOS_UNAVAILABLE; - } -#endif - mreq_source.imr_multiaddr.s_addr = htonl(group); mreq_source.imr_sourceaddr.s_addr = htonl(source); mreq_source.imr_interface.s_addr = htonl(interf); @@ -704,14 +670,6 @@ Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, j struct ip_mreq_source mreq_source; int n; int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; - -#ifdef _AIX - /* check AIX for support of source filtering */ - if (isSourceFilterSupported() != JNI_TRUE){ - return IOS_UNAVAILABLE; - } -#endif - mreq_source.imr_multiaddr.s_addr = htonl(group); mreq_source.imr_sourceaddr.s_addr = htonl(source); mreq_source.imr_interface.s_addr = htonl(interf); @@ -732,7 +690,9 @@ Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobjec jbyteArray group, jint index, jbyteArray source) { struct ipv6_mreq mreq6; +#ifndef __APPLE__ struct group_source_req req; +#endif int opt, n, optlen; void* optval; diff --git a/src/java.desktop/macosx/classes/com/apple/laf/ScreenPopupFactory.java b/src/java.desktop/macosx/classes/com/apple/laf/ScreenPopupFactory.java index ff9cadae1b88..feacbe5c65e4 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/ScreenPopupFactory.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/ScreenPopupFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,6 +76,9 @@ public Popup getPopup(final Component comp, final Component invoker, final int x popupRootPane.putClientProperty(CPlatformWindow.WINDOW_ALPHA, TRANSLUCENT); popupRootPane.putClientProperty(CPlatformWindow.WINDOW_SHADOW, Boolean.TRUE); popupRootPane.putClientProperty(CPlatformWindow.WINDOW_FADE_DELEGATE, invoker); + popupRootPane.putClientProperty( + CPlatformWindow.WINDOW_ACCESSIBILITY_ELEMENT, + !(invoker instanceof JToolTip) ); w.setBackground(UIManager.getColor("PopupMenu.translucentBackground")); popupRootPane.putClientProperty(CPlatformWindow.WINDOW_DRAGGABLE_BACKGROUND, Boolean.FALSE); diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index 2f74a16ef0fa..d17b9d4bc120 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,6 +94,7 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr, private static native void nativeDispose(long nsWindowPtr); private static native void nativeEnterFullScreenMode(long nsWindowPtr); private static native void nativeExitFullScreenMode(long nsWindowPtr); + private static native void nativeSetNSWindowAccessibilityElement(long nsWindowPtr, boolean axElement); static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse(); // Logger to report issues happened during execution but that do not affect functionality @@ -126,6 +127,8 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr, public static final String WINDOW_FULL_CONTENT = "apple.awt.fullWindowContent"; public static final String WINDOW_TRANSPARENT_TITLE_BAR = "apple.awt.transparentTitleBar"; public static final String WINDOW_TITLE_VISIBLE = "apple.awt.windowTitleVisible"; + public static final String WINDOW_ACCESSIBILITY_ELEMENT = + "apple.awt.windowAccessibilityElement"; // This system property is named as jdk.* because it is not specific to AWT // and it is also used in JavaFX @@ -266,6 +269,13 @@ public void applyProperty(final CPlatformWindow c, final Object value) { public void applyProperty(final CPlatformWindow c, final Object value) { c.setStyleBits(TITLE_VISIBLE, value == null ? true : Boolean.parseBoolean(value.toString())); } + }, + new Property(WINDOW_ACCESSIBILITY_ELEMENT) { + public void applyProperty(final CPlatformWindow c, + final Object value) { + c.setAccessibilityElement(value == null ? true : + Boolean.parseBoolean(value.toString())); + } } }) { public CPlatformWindow convertJComponentToTarget(final JRootPane p) { @@ -536,6 +546,10 @@ private void setStyleBits(final int mask, final boolean value) { execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value ? mask : 0)); } + private void setAccessibilityElement(final boolean value) { + execute(ptr -> nativeSetNSWindowAccessibilityElement(ptr, value)); + } + private native void _toggleFullScreenMode(final long model); public void toggleFullScreen() { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m index 6aba7d55856d..8da421183b7c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1211,6 +1211,29 @@ + (AWTWindow *) lastKeyWindow { JNI_COCOA_EXIT(env); } +/* + * Class: sun_lwawt_macosx_CPlatformWindow + * Method: nativeSetNSWindowAccessibilityElement + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowAccessibilityElement +(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean axElement) +{ +JNI_COCOA_ENTER(env); + + + NSWindow *nsWindow = OBJC(windowPtr); + + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ + + AWTWindow *window = (AWTWindow*)[nsWindow delegate]; + + [nsWindow setAccessibilityElement:axElement]; + }]; + + JNI_COCOA_EXIT(env); +} + /* * Class: sun_lwawt_macosx_CPlatformWindow * Method: nativeSetNSWindowMenuBar diff --git a/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c b/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c index c15795203a1f..19aad307ca21 100644 --- a/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c +++ b/src/java.desktop/share/native/libawt/awt/image/BufImgSurfaceData.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -342,7 +342,7 @@ static ColorData *BufImg_SetupICM(JNIEnv *env, jboolean allGray = (*env)->GetBooleanField(env, bisdo->icm, allGrayID); int *pRgb = (int *) - ((*env)->GetPrimitiveArrayCritical(env, bisdo->lutarray, NULL)); + ((*env)->GetIntArrayElements(env, bisdo->lutarray, NULL)); if (pRgb == NULL) { free(cData); @@ -351,7 +351,7 @@ static ColorData *BufImg_SetupICM(JNIEnv *env, cData->img_clr_tbl = initCubemap(pRgb, bisdo->lutsize, 32); if (cData->img_clr_tbl == NULL) { - (*env)->ReleasePrimitiveArrayCritical(env, bisdo->lutarray, pRgb, JNI_ABORT); + (*env)->ReleaseIntArrayElements(env, bisdo->lutarray, pRgb, JNI_ABORT); free(cData); return (ColorData*)NULL; } @@ -359,8 +359,7 @@ static ColorData *BufImg_SetupICM(JNIEnv *env, if (allGray == JNI_TRUE) { initInverseGrayLut(pRgb, bisdo->lutsize, cData); } - (*env)->ReleasePrimitiveArrayCritical(env, bisdo->lutarray, pRgb, - JNI_ABORT); + (*env)->ReleaseIntArrayElements(env, bisdo->lutarray, pRgb, JNI_ABORT); initDitherTables(cData); diff --git a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java index 47089dcd252d..7b505de2801a 100644 --- a/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1779,8 +1779,7 @@ private RMIServer findRMIServer(JMXServiceURL directoryURL, else if (path.startsWith("/stub/")) return findRMIServerJRMP(path.substring(6,end), environment); else { - final String msg = "URL path must begin with /jndi/ or /stub/ " + - "or /ior/: " + path; + final String msg = "URL path must begin with /jndi/ or /stub/: " + path; throw new MalformedURLException(msg); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 7eb7dd04a2b8..76ba420d8077 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -707,6 +707,7 @@ public void visitSwitch(JCSwitch tree) { for (JCCaseLabel pat : c.labels) { scan(pat); } + scan(c.guard); scanStats(c.stats); if (alive != Liveness.DEAD && c.caseKind == JCCase.RULE) { scanSyntheticBreak(make, tree); @@ -752,6 +753,7 @@ public void visitSwitchExpression(JCSwitchExpression tree) { for (JCCaseLabel pat : c.labels) { scan(pat); } + scan(c.guard); scanStats(c.stats); if (alive == Liveness.ALIVE) { if (c.caseKind == JCCase.RULE) { @@ -1228,6 +1230,7 @@ private void handleSwitch(JCTree tree, JCExpression selector, List cases for (List l = cases; l.nonEmpty(); l = l.tail) { JCCase c = l.head; scan(c.labels); + scan(c.guard); scan(c.stats); } if (tree.hasTag(SWITCH_EXPRESSION)) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 4b60fe5987f0..86a5147d089d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -689,15 +689,12 @@ private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure { ListBuffer exports = new ListBuffer<>(); Set seenPackages = new HashSet<>(); - for (JavaFileObject clazz : fileManager.list(msym.classLocation, "", EnumSet.of(Kind.CLASS), true)) { - String binName = fileManager.inferBinaryName(msym.classLocation, clazz); - String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package???? - if (seenPackages.add(pack)) { - ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null); - //TODO: opens? - directives.add(d); - exports.add(d); - } + findPackages(msym, msym.classLocation, EnumSet.of(Kind.CLASS), + seenPackages, directives, exports); + + if (msym.patchLocation != null) { + findPackages(msym, msym.patchLocation, EnumSet.of(Kind.CLASS, Kind.SOURCE), + seenPackages, directives, exports); } msym.exports = exports.toList(); @@ -709,6 +706,23 @@ private void setupAutomaticModule(ModuleSymbol msym) throws CompletionFailure { throw new IllegalStateException(ex); } } + //where: + private void findPackages(ModuleSymbol msym, + Location location, + Set fileObjectKinds, + Set seenPackages, + ListBuffer directives, + ListBuffer exports) throws IOException { + for (JavaFileObject clazz : fileManager.list(location, "", fileObjectKinds, true)) { + String binName = fileManager.inferBinaryName(location, clazz); + String pack = binName.lastIndexOf('.') != (-1) ? binName.substring(0, binName.lastIndexOf('.')) : ""; //unnamed package???? + if (seenPackages.add(pack)) { + ExportsDirective d = new ExportsDirective(syms.enterPackage(msym, names.fromString(pack)), null); + directives.add(d); + exports.add(d); + } + } + } private void completeAutomaticModule(ModuleSymbol msym) throws CompletionFailure { ListBuffer directives = new ListBuffer<>(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java index a0a2a6168c9d..5e3965f35e8b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/BaseFileManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -294,7 +294,13 @@ public boolean handleOption(Option option, String value) { return true; case PREVIEWMODE: - previewMode = Boolean.parseBoolean(value); + if ("true".equals(value)) { + previewMode = true; + } else if ("false".equals(value)) { + previewMode = false; + } else { + throw new IllegalArgumentException("Must be \"true\" or \"false\", got: " + value); + } locations.setPreviewMode(previewMode); return true; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java index 06ce293fbf10..5ffedf23c186 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/CommandProcessor.java @@ -56,6 +56,7 @@ import sun.jvm.hotspot.gc.g1.G1CollectedHeap; import sun.jvm.hotspot.oops.DefaultHeapVisitor; import sun.jvm.hotspot.oops.HeapVisitor; +import sun.jvm.hotspot.oops.Value; import sun.jvm.hotspot.oops.InstanceKlass; import sun.jvm.hotspot.oops.Klass; import sun.jvm.hotspot.oops.Metadata; @@ -256,12 +257,20 @@ void usage() { out.println("Usage: " + usage); } - void printNode(SimpleTreeNode node) { + void printNode(SimpleTreeNode node, int indent) { + String blanks = (indent == 0) ? "" + : String.format("%" + (indent * 2) + "s", ""); int count = node.getChildCount(); for (int i = 0; i < count; i++) { + out.print(blanks); try { SimpleTreeNode field = node.getChild(i); - out.println(field); + if (field instanceof OopTreeNodeAdapter of && of.getOop() instanceof Value oop) { + out.println(of.getName() + ": "); + printNode(new OopTreeNodeAdapter(oop, null), indent + 1); + } else { + out.println(field); + } } catch (Exception e) { out.println(); out.println("Error: " + e); @@ -271,6 +280,10 @@ void printNode(SimpleTreeNode node) { } } } + + void printNode(SimpleTreeNode node) { + printNode(node, 0); + } } void quote(String s) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java index 128cb58649c5..aa198c3e33ca 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Field.java @@ -221,6 +221,7 @@ public int getFieldIndex() { public boolean isStable() { return fieldIsStable(values.fieldFlags); } public boolean isContended() { return fieldIsContended(values.fieldFlags); } public boolean isFlat() { return fieldIsFlat(values.fieldFlags); } + public boolean hasNullMarker() { return fieldHasNullMarker(values.fieldFlags); } public boolean equals(Object obj) { if (obj == null) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlattenedValue.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlattenedValue.java new file mode 100644 index 000000000000..347cc2c84851 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/FlattenedValue.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.oops; + +import java.io.PrintStream; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.debugger.OopHandle; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.utilities.Assert; + + +/** + * FlattenedValue represents a flattened object in HotSpot. + * Note that there is no corresponding class in HotSpot. This class is used + * in SA to handle a flattened object in same way as an oop. + */ +public class FlattenedValue extends Value { + + private final ValueKlass klass; + + FlattenedValue(Address payload, ObjectHeap heap, ValueKlass klass) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(klass != null, "klass should not be null"); + } + // Create oop handle for holder object that contains the flattened + // field payload. + OopHandle handle = payload.addOffsetToAsOopHandle(- klass.payloadOffset()); + + super(handle, heap); + this.klass = klass; + } + + @Override + public Mark getMark() { + throw new UnsupportedOperationException("Flattened object does not have mark word"); + } + + @Override + public Klass getKlass() { + return klass; + } + + @Override + public long identityHash() { + throw new UnsupportedOperationException("Flattened object does not support identity hash"); + } + + @Override + public long slowIdentityHash() { + throw new UnsupportedOperationException("Flattened object does not support identity hash"); + } + + @Override + public void iterateFields(OopVisitor visitor, boolean doVMFields) { + klass.iterateNonStaticFields(visitor, this); + } + + @Override + public void printValueOn(PrintStream tty) { + tty.print("Inlined object (flattened)"); + } +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index 96d177bc190e..3eb4de10ce0e 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -106,6 +106,8 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeExc } headerSize = type.getSize(); accessFlags = new CIntField(type.getCIntegerField("_access_flags"), 0); + valueFieldLayoutInfoArray = type.getAddressField("_value_field_layout_info_array"); + adrValueKlassMembers = type.getAddressField("_adr_value_klass_members"); // read internal field flags constants FIELD_FLAG_IS_INITIALIZED = db.lookupIntConstant("FieldInfo::FieldFlags::_ff_initialized"); @@ -173,6 +175,8 @@ public InstanceKlass(Address addr) { private static CIntField nestHostIndex; private static CIntField accessFlags; private static AddressField breakpoints; + private static AddressField valueFieldLayoutInfoArray; + private static AddressField adrValueKlassMembers; // type safe enum for ClassState from instanceKlass.hpp public static class ClassState { @@ -888,6 +892,15 @@ public U2Array getNestMembers() { return VMObjectFactory.newObject(U2Array.class, addr); } + public Address getAdrValueKlassMembers() { + return getAddress().getAddressAt(adrValueKlassMembers.getOffset()); + } + + public ValueFieldLayoutInfoArray getValueFieldLayoutInfoArray() { + Address addr = valueFieldLayoutInfoArray.getValue(getAddress()); + return VMObjectFactory.newObject(ValueFieldLayoutInfoArray.class, addr); + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/NarrowOopField.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/NarrowOopField.java index 3c00e0b91fe1..734f1821d397 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/NarrowOopField.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/NarrowOopField.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,10 +40,6 @@ public NarrowOopField(InstanceKlass holder, int fieldArrayIndex) { super(holder, fieldArrayIndex); } - public Oop getValue(Oop obj) { - return obj.getHeap().newOop(getValueAsOopHandle(obj)); - } - /** Debugging support */ public OopHandle getValueAsOopHandle(Oop obj) { return obj.getHandle().getCompOopHandleAt(getOffset()); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java index baf0c2e4e263..2f1b662aaeb9 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java @@ -182,6 +182,10 @@ public Oop newOop(OopHandle handle) { if (klass instanceof TypeArrayKlass) return new TypeArray(handle, this); if (klass instanceof FlatArrayKlass) return new FlatArray(handle, this); if (klass instanceof ObjArrayKlass) return new ObjArray(handle, this); + + // ValueKlass should be evaluated before InstanceKlass + // because ValueKlass inherits InstanceKlass. + if (klass instanceof ValueKlass) return new Value(handle, this); if (klass instanceof InstanceKlass) return new Instance(handle, this); } @@ -193,6 +197,17 @@ public Oop newOop(OopHandle handle) { throw new UnknownOopException(handle.toString()); } + // This method is used to instantiate a holder object that contains + // the flattened field payload. + public Oop newOop(Address payload, ValueKlass klass, OopField field) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(payload != null, "payload should not be null"); + } + return field.hasNullMarker() && klass.isPayloadMarkedAsNull(payload) + ? null + : new FlattenedValue(payload, this, klass); + } + // Print all objects in the object heap public void print() { HeapPrinter printer = new HeapPrinter(System.out); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java index 4a42240314a8..9d5a855404aa 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java @@ -99,6 +99,7 @@ public long getObjectSize() { } // Type test operations + public boolean isValue() { return false; } public boolean isInstance() { return false; } public boolean isInstanceRef() { return false; } public boolean isArray() { return false; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java index 7e85349a1048..7bcb218e1aea 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.runtime.VMObject; +import sun.jvm.hotspot.utilities.SystemDictionaryHelper; // The class for an oop field simply provides access to the value. public class OopField extends Field { @@ -46,7 +47,18 @@ public Oop getValue(Oop obj) { if (!isVMField() && !obj.isInstance() && !obj.isArray()) { throw new InternalError(); } - return obj.getHeap().newOop(getValueAsOopHandle(obj)); + var heap = obj.getHeap(); + if (isFlat()) { + var layout = ((InstanceKlass)obj.getKlass()).getValueFieldLayoutInfoArray().at(getFieldIndex()); + ValueKlass vk = layout.getKlass(); + + // OopHandle does not allow to call addOffsetTo() due to prevent interior + // object pointers. So addOffsetToAsOopHandle() is required here. + Address payload = obj.getHandle().addOffsetToAsOopHandle(getOffset()); + return heap.newOop(payload, vk, this); + } else { + return heap.newOop(getValueAsOopHandle(obj)); + } } /** Debugging support */ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Value.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Value.java new file mode 100644 index 000000000000..2bf256576163 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Value.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.oops; + +import java.io.PrintStream; + +import sun.jvm.hotspot.debugger.OopHandle; +import sun.jvm.hotspot.oops.ObjectHeap; + + +/** + * Value represents "valueOop" in HotSpot in SA. + * See FlattenedValue for flattened object. + */ +public class Value extends Instance { + + Value(OopHandle handle, ObjectHeap heap) { + super(handle, heap); + } + + @Override + public boolean isValue() { + return true; + } + + @Override + public void printValueOn(PrintStream tty) { + tty.print("Value object"); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java new file mode 100644 index 000000000000..11817f3d0b98 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.oops; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.VMObject; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.types.WrongTypeException; + + +public class ValueFieldLayoutInfo extends VMObject { + + private static MetadataField klassField; + + static { + VM.registerVMInitializedObserver((_, _) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("ValueFieldLayoutInfo"); + + klassField = new MetadataField(type.getAddressField("_klass"), 0); + } + + public ValueFieldLayoutInfo(Address addr) { + super(addr); + } + + public ValueKlass getKlass() { + return (ValueKlass)klassField.getValue(this); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueKlass.java index bc05f0cf3988..99add5b92139 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueKlass.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueKlass.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 @@ -34,9 +34,39 @@ import sun.jvm.hotspot.utilities.Observable; import sun.jvm.hotspot.utilities.Observer; -// An InstanceKlass is the VM level representation of a Java class. +// An ValueKlass is the VM level representation of a flattenable Java class. public class ValueKlass extends InstanceKlass { + + public static class Members extends VMObject { + + private static CIntField payloadOffsetField; + private static CIntField nullMarkerOffsetField; + + static { + VM.registerVMInitializedObserver((o, d) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + Type type = db.lookupType("ValueKlass::Members"); + payloadOffsetField = new CIntField(type.getCIntegerField("_payload_offset"), 0); + nullMarkerOffsetField = new CIntField(type.getCIntegerField("_null_marker_offset"), 0); + } + + public Members(Address addr) { + super(addr); + } + + public int payloadOffset() { + return (int)payloadOffsetField.getValue(this); + } + + public int nullMarkerOffset() { + return (int)nullMarkerOffsetField.getValue(this); + } + + } + static { VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { @@ -53,4 +83,32 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeExc public ValueKlass(Address addr) { super(addr); } + + public Members members() { + return VMObjectFactory.newObject(Members.class, getAdrValueKlassMembers()); + } + + public int nullMarkerOffset() { + return members().nullMarkerOffset(); + } + + public int payloadOffset() { + return members().payloadOffset(); + } + + public int nullMarkerOffsetInPayload() { + return nullMarkerOffset() - payloadOffset(); + } + + public OopHandle nullMarkerAddress(Address payload) { + // "payload" might be OopHandle (e.g. when it comes from OopField), then + // we cannot use addOffsetTo() because it is not allowed due to prevent + // interior object pointers. Hence addOffsetToAsOopHandle() is called here. + return payload.addOffsetToAsOopHandle(nullMarkerOffsetInPayload()); + } + + public boolean isPayloadMarkedAsNull(Address payload) { + return nullMarkerAddress(payload).getJByteAt(0) == (byte)0; + } + } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java new file mode 100644 index 000000000000..e9053febcafb --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.utilities; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.oops.ValueFieldLayoutInfo; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.VMObjectFactory; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.types.WrongTypeException; + + +public class ValueFieldLayoutInfoArray extends GenericArray { + + private static long dataFieldOffset; + private static Type elemType; + + static { + VM.registerVMInitializedObserver((_, _) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { + elemType = db.lookupType("ValueFieldLayoutInfo"); + Type type = db.lookupType("Array"); + dataFieldOffset = type.getAddressField("_data").getOffset(); + } + + public ValueFieldLayoutInfoArray(Address addr) { + super(addr, dataFieldOffset); + } + + public ValueFieldLayoutInfo at(int i) { + if (i < 0 || i >= length()) { + throw new ArrayIndexOutOfBoundsException(i); + } + + var addr = getDataStart().addOffsetTo(i * getElemType().getSize()); + return new ValueFieldLayoutInfo(addr); + } + + @Override + public Type getElemType() { + return elemType; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java index 155e9098d916..6825fc92eb6d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.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 @@ -26,6 +26,7 @@ package jdk.jfr; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; @@ -148,7 +149,8 @@ public AnnotationElement(Class annotationType, Map fieldType = object.getClass(); + object = safeObject(object); + Class fieldType = method.getReturnType(); if (fieldType == Class.class) { throw new IllegalArgumentException("Annotation value for " + fieldName + " can't be class"); @@ -156,8 +158,9 @@ public AnnotationElement(Class annotationType, Map objectType = Utils.unboxType(object.getClass()); + if (objectType != fieldType) { + throw new IllegalArgumentException("Return type of annotation " + fieldType.getName() + " must match type of object " + objectType.getName()); } if (fieldType.isArray()) { @@ -172,7 +175,6 @@ public AnnotationElement(Class annotationType, Map annotationType) { * @return list of values, not {@code null} */ public List getValues() { - return annotationValues; + return safeList(annotationValues); } /** @@ -294,7 +296,7 @@ public Object getValue(String name) { Objects.requireNonNull(name, "name"); int index = type.indexOf(name); if (index != -1) { - return annotationValues.get(index); + return safeObject(annotationValues.get(index)); } StringJoiner valueNames = new StringJoiner(",", "[", "]"); for (ValueDescriptor v : type.getFields()) { @@ -384,4 +386,35 @@ private static boolean isKnownJFRAnnotation(Class annotati boolean isInBoot() { return inBootClassLoader; } + + private static Object safeObject(Object object) { + return object.getClass().isArray() ? safeArray(object) : object; + } + + private static Object safeArray(Object source) { + Class type = source.getClass().getComponentType(); + int length = Array.getLength(source); + if (length == 0) { + return source; + } + Object result = Array.newInstance(type, length); + System.arraycopy(source, 0, result, 0, length); + return result; + } + + // Assumes list is created by List.copyOf(...); + private static List safeList(List list) { + if (list.isEmpty()) { + return list; + } + if (list.size() == 1) { + Object element = list.get(0); + return element.getClass().isArray() ? List.of(safeObject(element)) : list; + } + List result = new ArrayList<>(list.size()); + for (Object element : list) { + result.add(safeObject(element)); + } + return List.copyOf(result); + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java index 3e5f01a37790..42cdd703b372 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java @@ -59,7 +59,7 @@ public boolean is(byte value) { private static final class CharsetParser extends Parser { private final Charset charset; - private int lastSize; + private int lastSize = -1; private byte[] buffer = new byte[16]; private String lastString; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/JFC.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/JFC.java index d89f527ae4b8..2677d6f91f93 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/JFC.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/JFC.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 @@ -193,7 +193,7 @@ public static Configuration createKnown(String name) throws IOException, ParseEx Path path = JFC_DIRECTORY; if (path != null && Files.exists(path)) { for (String extension : Arrays.asList("", JFCParser.FILE_EXTENSION)) { - Path file = path.resolveSibling(name + extension); + Path file = path.resolve(name + extension); if (Files.exists(file) && !Files.isDirectory(file)) { try (Reader r = Files.newBufferedReader(file)) { String jfcName = nameFromPath(file); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java index 4c921ba4d876..36a8e52eac65 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Function.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 @@ -179,6 +179,7 @@ public void add(Object value) { long secondsToAdd = d.getSeconds(); long nanosToAdd = d.getNano(); if ((secondsToAdd | nanosToAdd) == 0) { + count++; return; } long s = Math.addExact(seconds, secondsToAdd); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java index 1ba245babcc0..885751e9d3c1 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,13 +60,15 @@ public CutoffSetting(PlatformEventType eventType, String defaultValue) { @Override public String combine(Set values) { - long max = 0; + Long max = null; String text = null; for (String value : values) { long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); - if (nanos != MISSING && nanos > max) { - text = value; - max = nanos; + if (nanos != MISSING) { + if (max == null || nanos > max) { + text = value; + max = nanos; + } } } return Objects.requireNonNullElse(text, defaultValue); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java index 33d79222617b..12a0859a3c87 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.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 @@ -101,10 +101,13 @@ public String combine(Set values) { if (min != null) { return text; } - if (beginChunk && !endChunk) { + if (beginChunk && endChunk) { + return EVERY_CHUNK; + } + if (beginChunk) { return BEGIN_CHUNK; } - if (!beginChunk && endChunk) { + if (endChunk) { return END_CHUNK; } return defaultValue; diff --git a/test/hotspot/gtest/metaprogramming/test_enableIf.cpp b/test/hotspot/gtest/metaprogramming/test_enableIf.cpp index 05603f01c646..bef7bb85262c 100644 --- a/test/hotspot/gtest/metaprogramming/test_enableIf.cpp +++ b/test/hotspot/gtest/metaprogramming/test_enableIf.cpp @@ -38,10 +38,10 @@ class EnableIfTest: AllStatic { }; static const bool A_test_true_is_char = sizeof(A::test()) == sizeof(char); - STATIC_ASSERT(A_test_true_is_char); + static_assert(A_test_true_is_char); static const bool A_test_false_is_long = sizeof(A::test()) == sizeof(long); - STATIC_ASSERT(A_test_false_is_long); + static_assert(A_test_false_is_long); }; template::value)> diff --git a/test/hotspot/gtest/metaspace/test_chunkManager_stress.cpp b/test/hotspot/gtest/metaspace/test_chunkManager_stress.cpp index c05b4a5a2871..de15fb8e5681 100644 --- a/test/hotspot/gtest/metaspace/test_chunkManager_stress.cpp +++ b/test/hotspot/gtest/metaspace/test_chunkManager_stress.cpp @@ -183,8 +183,8 @@ class ChunkManagerRandomChunkAllocTest { } // adjust test if we change levels - STATIC_ASSERT(HIGHEST_CHUNK_LEVEL == CHUNK_LEVEL_1K); - STATIC_ASSERT(LOWEST_CHUNK_LEVEL == CHUNK_LEVEL_16M); + static_assert(HIGHEST_CHUNK_LEVEL == CHUNK_LEVEL_1K); + static_assert(LOWEST_CHUNK_LEVEL == CHUNK_LEVEL_16M); void one_test() { diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index 75d4f7bb9d57..ea0732e1392b 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -29,6 +29,7 @@ #include "runtime/thread.hpp" #include "runtime/threads.hpp" #include "testutils.hpp" +#include "threadHelper.inline.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -123,6 +124,96 @@ TEST_VM(os, page_size_for_region_unaligned) { } } +TEST_VM(os, test_print_markword) { +#ifdef _LP64 + if (UseCompactObjectHeaders) { + JavaThread* THREAD = JavaThread::current(); + ThreadInVMfromNative invm(THREAD); + markWord m0 = vmClasses::Byte_klass()->prototype_header(); + const struct { markWord mw; const char* expected; } patterns[] = { + { markWord(0), "is null"}, + { m0, "mark(is_lock_neutral no_hash age=0) java.lang.Byte" }, + { m0.set_age(2), "age=2" }, + { m0.copy_set_hash(0x12345), "is an unknown value" } + }; + constexpr int nmark = sizeof(patterns) / sizeof(patterns[0]); + for (int i = 0; i < nmark; i++) { + stringStream st; + MutexLocker lock(ClassLoaderDataGraph_lock); + os::print_location(&st, (intptr_t) patterns[i].mw.to_pointer(), true); + ASSERT_THAT(st.base(), testing::HasSubstr(patterns[i].expected)); + } + } +#endif +} + +static void assert_test_pattern(oop& obj, const char* pattern, bool is_present=true) { + stringStream st; + os::print_location(&st, p2i(obj), true); + if (is_present) { + ASSERT_THAT(st.base(), testing::HasSubstr(pattern)); + } else { + ASSERT_THAT(st.base(), testing::Not(testing::HasSubstr(pattern))); + } +} + +TEST_VM(os, test_print_location) { + JavaThread* THREAD = JavaThread::current(); + ThreadInVMfromNative invm(THREAD); + + oop obj = vmClasses::Byte_klass()->allocate_instance(THREAD); + + { + // wizard mode off, so don't print markword + MutexLocker lock(ClassLoaderDataGraph_lock); + assert_test_pattern(obj, "is_lock_neutral no_hash", WizardMode); + } + + // WizardMode (not available in release mode) prints details +#ifndef PRODUCT + FlagSetting fs(WizardMode, true); + bool using_wizardMode = true; +#else + bool using_wizardMode = false; +#endif + + HandleMark hm(THREAD); + Handle h_obj(THREAD, obj); + + // Thread tries to lock it. + { + MutexLocker lock(ClassLoaderDataGraph_lock); + ObjectLocker ol(h_obj, THREAD); + if (using_wizardMode) { + assert_test_pattern(obj, "locked"); + } else { + assert_test_pattern(obj, "locked", false); + } + assert_test_pattern(obj, "is_lock_neutral", false); + } + + // Unlocked again + { + MutexLocker lock(ClassLoaderDataGraph_lock); + if (using_wizardMode) { + assert_test_pattern(obj, "is_lock_neutral"); + } else { + assert_test_pattern(obj, "is_lock_neutral", false); + } + } + + // Hash the object then print it. + { + intx hash = h_obj->identity_hash(); + MutexLocker lock(ClassLoaderDataGraph_lock); + if (using_wizardMode) { + assert_test_pattern(obj, "is_lock_neutral hash="); + } else { + assert_test_pattern(obj, "is_lock_neutral hash=", false); + } + } +} + TEST(os, test_random) { const double m = 2147483647; double mean = 0.0, variance = 0.0, t; diff --git a/test/hotspot/gtest/utilities/test_bitMap_search.cpp b/test/hotspot/gtest/utilities/test_bitMap_search.cpp index 952ff2d78499..f267186f1744 100644 --- a/test/hotspot/gtest/utilities/test_bitMap_search.cpp +++ b/test/hotspot/gtest/utilities/test_bitMap_search.cpp @@ -43,7 +43,7 @@ static const size_t search_offsets[] = static const size_t search_noffsets = ARRAY_SIZE(search_offsets); static const size_t search_nchunks = BITMAP_SIZE / search_chunk_size; -STATIC_ASSERT(search_nchunks * search_chunk_size == BITMAP_SIZE); +static_assert(search_nchunks * search_chunk_size == BITMAP_SIZE); namespace { class TestIteratorFn : public BitMapClosure { diff --git a/test/hotspot/gtest/utilities/test_bitMap_setops.cpp b/test/hotspot/gtest/utilities/test_bitMap_setops.cpp index 1def9c89cfcd..9937edd92712 100644 --- a/test/hotspot/gtest/utilities/test_bitMap_setops.cpp +++ b/test/hotspot/gtest/utilities/test_bitMap_setops.cpp @@ -54,7 +54,7 @@ class BitMapMemory { BitMapView make_view(idx_t bits, bm_word_t value) { vmassert(BitMap::calc_size_in_words(bits) <= _words, "invalid request"); - STATIC_ASSERT(sizeof(bm_word_t) == sizeof(HeapWord)); + static_assert(sizeof(bm_word_t) == sizeof(HeapWord)); Copy::fill_to_aligned_words((HeapWord*)_memory, _words, value); return BitMapView(_memory, bits); } @@ -143,7 +143,7 @@ TEST(BitMap, is_same__unaligned) { { BitMapView aligned = BitMapView(mx.memory(), aligned_size); const idx_t index = aligned_size - 2; - STATIC_ASSERT(unaligned_size <= index); + static_assert(unaligned_size <= index); WithBitClear wbc(aligned, index); EXPECT_TRUE(x.is_same(y)); @@ -257,7 +257,7 @@ TEST(BitMap, contains__unaligned) { { BitMapView aligned = BitMapView(mx.memory(), aligned_size); const idx_t index = aligned_size - 2; - STATIC_ASSERT(unaligned_size <= index); + static_assert(unaligned_size <= index); WithBitClear wbc(aligned, index); EXPECT_TRUE(x.contains(y)); @@ -302,7 +302,7 @@ TEST(BitMap, intersects__unaligned) { BitMapView aligned_x = BitMapView(mx.memory(), aligned_size); BitMapView aligned_y = BitMapView(my.memory(), aligned_size); const idx_t index = aligned_size - 2; - STATIC_ASSERT(unaligned_size <= index); + static_assert(unaligned_size <= index); ASSERT_TRUE(aligned_x.at(index)); WithBitSet wbs(aligned_y, index); diff --git a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp index b8b65ae8ca85..897412cb9f2c 100644 --- a/test/hotspot/gtest/utilities/test_globalDefinitions.cpp +++ b/test/hotspot/gtest/utilities/test_globalDefinitions.cpp @@ -195,24 +195,24 @@ TEST(globalDefinitions, array_size) { { int test_array[test_size] = {}; - static_assert(test_size == ARRAY_SIZE(test_array), "must be"); + static_assert(test_size == ARRAY_SIZE(test_array)); } { double test_array[test_size] = {}; - static_assert(test_size == ARRAY_SIZE(test_array), "must be"); + static_assert(test_size == ARRAY_SIZE(test_array)); } struct ArrayElt { int x; }; { ArrayElt test_array[test_size] = {}; - static_assert(test_size == ARRAY_SIZE(test_array), "must be"); + static_assert(test_size == ARRAY_SIZE(test_array)); } { const ArrayElt test_array[] = { {0}, {1}, {2}, {3}, {4}, {5} }; - static_assert(6 == ARRAY_SIZE(test_array), "must be"); + static_assert(6 == ARRAY_SIZE(test_array)); } } diff --git a/test/hotspot/gtest/utilities/test_intn_t.cpp b/test/hotspot/gtest/utilities/test_intn_t.cpp index 89958e0757e0..dc0bc01954a8 100644 --- a/test/hotspot/gtest/utilities/test_intn_t.cpp +++ b/test/hotspot/gtest/utilities/test_intn_t.cpp @@ -26,18 +26,18 @@ #include "unittest.hpp" // Sanity tests for off-by-one errors -static_assert(intn_t<1>::min == -1 && intn_t<1>::max == 0, ""); -static_assert(intn_t<2>::min == -2 && intn_t<2>::max == 1, ""); -static_assert(intn_t<3>::min == -4 && intn_t<3>::max == 3, ""); -static_assert(uintn_t<1>::max == 1, ""); -static_assert(uintn_t<2>::max == 3, ""); -static_assert(uintn_t<3>::max == 7, ""); +static_assert(intn_t<1>::min == -1 && intn_t<1>::max == 0); +static_assert(intn_t<2>::min == -2 && intn_t<2>::max == 1); +static_assert(intn_t<3>::min == -4 && intn_t<3>::max == 3); +static_assert(uintn_t<1>::max == 1); +static_assert(uintn_t<2>::max == 3); +static_assert(uintn_t<3>::max == 7); template static void test_intn_t() { static_assert(std::numeric_limits>::min() <= intn_t(-1) && intn_t(-1) < intn_t(0) && - intn_t(0) <= std::numeric_limits>::max(), "basic sanity"); + intn_t(0) <= std::numeric_limits>::max()); constexpr int period = intn_t::max - intn_t::min + 1; for (int i = std::numeric_limits::min(); i < std::numeric_limits::max(); i++) { ASSERT_EQ(intn_t(i), intn_t(i + period)); diff --git a/test/hotspot/gtest/utilities/test_linkedlist.cpp b/test/hotspot/gtest/utilities/test_linkedlist.cpp deleted file mode 100644 index 7b4c6a85b09c..000000000000 --- a/test/hotspot/gtest/utilities/test_linkedlist.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - - */ - -#include "unittest.hpp" -#include "utilities/linkedlist.hpp" - -class Integer : public StackObj { - private: - int _value; - public: - - Integer(int i) : _value(i) { - } - - int value() const { - return _value; - } - - bool equals(const Integer& i) const { - return _value == i.value(); - } - - static int compare(const Integer& i1, const Integer& i2) { - return i1.value() - i2.value(); - } -}; - -static void check_list_values(const int* expected, const LinkedList* list) { - LinkedListNode* head = list->head(); - int index = 0; - while (head != nullptr) { - ASSERT_EQ(expected[index], head->peek()->value()) - << "Unexpected value at index " << index; - head = head->next(); - index++; - } -} - -const Integer one(1), two(2), three(3), four(4), five(5), six(6), notfound(404); - -// Test regular linked list -TEST(LinkedList, simple) { - LinkedListImpl ll; - - ASSERT_TRUE(ll.is_empty()) << "Start with empty list"; - - ll.add(six); - ASSERT_TRUE(!ll.is_empty()) << "Should not be empty"; - - Integer* i = ll.find(six); - ASSERT_TRUE(i != nullptr) << "Should find it"; - ASSERT_EQ(six.value(), i->value()) << "Should be 6"; - - i = ll.find(three); - ASSERT_TRUE(i == nullptr) << "Not in the list"; - - LinkedListNode* node = ll.find_node(six); - ASSERT_TRUE(node != nullptr) << "6 is in the list"; - - ll.insert_after(three, node); - ll.insert_before(one, node); - int expected[3] = {1, 6, 3}; - check_list_values(expected, &ll); -} - -TEST(LinkedList, generic) { - LinkedListImpl il; - const int N = 100; - for (int i=0; i cit(il.head()); - for (int i=N-1; i>=0; --i) { - const int* e = cit.next(); - EXPECT_EQ(*e, i); - } - EXPECT_TRUE(cit.is_empty()); - EXPECT_EQ(il.size(), (size_t)N); - EXPECT_EQ(*(il.head()->peek()), N-1); - - typedef LinkedListImpl list_t; - LinkedList* list = new (mtTest) list_t(); - list->add(Integer(1)); - list->add(Integer(2)); - EXPECT_EQ(list->size(), (size_t)2); - list->~LinkedList(); - EXPECT_EQ(list->size(), (size_t)0); - - // copyable - //list_t a; - //a.add(Integer(1)); - //list_t b(a); - //EXPECT_EQ(b.size(), (size_t)1); - //EXPECT_TRUE(b.head()->peek()->equals(Integer(1))); - - list_t lifo, dummy; - const Integer* e; - lifo.add(one); - lifo.add(two); - LinkedListIterator it(lifo.head()); - - EXPECT_FALSE(it.is_empty()); - // pop 2 - e = it.next(); - EXPECT_TRUE(e->equals(two)); - EXPECT_FALSE(it.is_empty()); - // pop 1 - e = it.next(); - EXPECT_TRUE(e->equals(one)); - //empty - EXPECT_TRUE(it.is_empty()); - - LinkedListIterator it2(dummy.head()); - EXPECT_TRUE(it2.is_empty()); - EXPECT_EQ(it2.next(), (Integer* )nullptr); -} - -TEST(LinkedList, algorithm) { - LinkedListImpl il; - il.add(1); - il.add(2); - il.add(3); - EXPECT_EQ(*il.find(1), 1); - EXPECT_EQ(il.find(404), (int* )nullptr); - EXPECT_TRUE(il.remove(1)); - EXPECT_FALSE(il.remove(404)); - - LinkedListImpl ll; - ll.add(one); - - EXPECT_TRUE(ll.find(one)); - EXPECT_FALSE(ll.find(notfound)); - - EXPECT_TRUE(ll.remove(one)); - EXPECT_FALSE(ll.find(one)); - EXPECT_FALSE(ll.remove(notfound)); - EXPECT_FALSE(ll.find(notfound)); -} - -// Test sorted linked list -TEST(SortedLinkedList, simple) { - LinkedListImpl ll; - ll.add(one); - ll.add(six); - ll.add(three); - ll.add(two); - ll.add(four); - ll.add(five); - - SortedLinkedList sl; - ASSERT_TRUE(sl.is_empty()) << "Start with empty list"; - - size_t ll_size = ll.size(); - sl.move(&ll); - size_t sl_size = sl.size(); - - ASSERT_EQ(ll_size, sl_size) << "Should be the same size"; - ASSERT_TRUE(ll.is_empty()) << "No more entries"; - - // sorted result - int sorted_result[] = {1, 2, 3, 4, 5, 6}; - check_list_values(sorted_result, &sl); - if (HasFatalFailure()) { - return; - } - - LinkedListNode* node = sl.find_node(four); - ASSERT_TRUE(node != nullptr) << "4 is in the list"; - sl.remove_before(node); - sl.remove_after(node); - int remains[] = {1, 2, 4, 6}; - check_list_values(remains, &sl); -} diff --git a/test/hotspot/gtest/utilities/test_powerOfTwo.cpp b/test/hotspot/gtest/utilities/test_powerOfTwo.cpp index 9ead9952e739..e8147800e126 100644 --- a/test/hotspot/gtest/utilities/test_powerOfTwo.cpp +++ b/test/hotspot/gtest/utilities/test_powerOfTwo.cpp @@ -64,12 +64,12 @@ template static void test_is_power_of_2() { EXPECT_FALSE(is_power_of_2(T(0))); EXPECT_FALSE(is_power_of_2(~T(0))); - static_assert(!is_power_of_2(T(0)), ""); - static_assert(!is_power_of_2(~T(0)), ""); + static_assert(!is_power_of_2(T(0))); + static_assert(!is_power_of_2(~T(0))); // Should be false regardless of whether T is signed or unsigned. EXPECT_FALSE(is_power_of_2(std::numeric_limits::min())); - static_assert(!is_power_of_2(std::numeric_limits::min()), ""); + static_assert(!is_power_of_2(std::numeric_limits::min())); // Test true for (T i = max_power_of_2(); i > 0; i = (i >> 1)) { diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 53bb850a6ecc..775479953383 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -85,6 +85,12 @@ vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemor # to make progress when all other threads are currently suspended. vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.java 8338713 generic-all +#### +## NSK JDWP tests failing with wrapper + +# JVMTI bug when popping to a frozen frame +vmTestbase/nsk/jdwp/StackFrame/PopFrames/popframes001/TestDescription.java 8391440 generic-all + ############################################################################# # Preview project specific failures go here at the end of the file. diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 576a4fa6acd5..cda1fc4fda5b 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -73,6 +73,12 @@ compiler/vectorapi/VectorStoreMaskIdentityTest.java 8388281 generic-all compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java 8391806 generic-aarch64 +compiler/vectorapi/VectorMaskCompareNotTest.java 8392087 windows-x64 + +compiler/vectorapi/VectorExpressionFuzzer.java 8337281 linux-aarch64,macosx-aarch64 + +compiler/igvn/TestIncorrectDeadPathTypeNode.java 8392354 generic-all + ############################################################################# # :hotspot_gc diff --git a/test/hotspot/jtreg/compiler/c2/TestConstantFoldStrictFinalField.java b/test/hotspot/jtreg/compiler/c2/TestConstantFoldStrictFinalField.java new file mode 100644 index 000000000000..bf50c25a6101 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestConstantFoldStrictFinalField.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.helpers.StrictInit; + +/* + * @test + * @bug 8392413 + * @summary Test constant fold strict final fields. + * @library /test/lib / + * @enablePreview + * @compile ${test.file} + * @run driver jdk.test.lib.helpers.StrictProcessor + * ${test.main.class}$Int + * @run driver ${test.main.class} + */ +public class TestConstantFoldStrictFinalField { + private static class Int { + @StrictInit + final int v; + + Int(int v) { + this.v = v; + super(); + } + } + + private static final Int OBJECT = new Int(1); + + public static void main(String[] args) { + var framework = new TestFramework(); + framework.setDefaultWarmup(1); + framework.addFlags("--enable-preview"); + framework.start(); + } + + @Test + @IR(failOn = IRNode.LOAD) + private static int testFoldStrictFinal() { + return OBJECT.v; + } + + @Run(test = "testFoldStrictFinal") + public void runFoldStrictFinal() { + Asserts.assertEQ(OBJECT.v, testFoldStrictFinal()); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestWrappedBoxedPrimitive.java b/test/hotspot/jtreg/compiler/c2/TestWrappedBoxedPrimitive.java new file mode 100644 index 000000000000..7408d2a6e406 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestWrappedBoxedPrimitive.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8384101 + * @summary Test that references to boxed primitives are not considered final. + * @requires vm.compiler2.enabled + * @library /test/lib / + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:+StressIncrementalInlining -XX:StressSeed=2 + * ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:+StressIncrementalInlining + * ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.c2; + +import jdk.test.lib.Asserts; + +public class TestWrappedBoxedPrimitive { + private static final class Holder { + Integer notZero = 0xbad; + + private void touch() { + notZero = 123456; + } + } + + private static class BaseHolder { + Integer notZero = 0xbad; + Integer value = 42; + + Integer get() { + return value; + } + } + + private static final class EvilHolder extends BaseHolder { + @Override + Integer get() { + notZero = 1234; + return value; + } + } + + private static final class FooHolder extends BaseHolder { } + private static final class BarHolder extends BaseHolder { } + + static BaseHolder make(boolean evil, int i) { + if (evil) { + return new EvilHolder(); + } + return switch (i % 3) { + case 0 -> new FooHolder(); + case 1 -> new BarHolder(); + default -> new BaseHolder(); + }; + } + + private static int testHolderKlass() { + Holder holder = new Holder(); + holder.touch(); + return holder.notZero; + } + + private static void touchArray(Integer[] arr) { + arr[0] = 12345; + } + + private static int testArray() { + Integer[] ary = new Integer[] { 0xbad }; + touchArray(ary); + return ary[0]; + + } + + private static int testGetter(boolean evil, int i) { + BaseHolder holder = make(evil, i); + holder.get(); + return holder.notZero; + } + + public static void main(String[] args) { + int intHolderKlass = testHolderKlass(); + int intHolderArray = testArray(); + int intHolderGetter = testGetter(true, 0); + for (int i = 0; i < 10_000; i++) { + testHolderKlass(); + } + for (int i = 0; i < 10_000; i++) { + testArray(); + } + for (int i = 0; i < 10_000; i++) { + testGetter(i % 2 == 0, i); + } + int c2HolderKlass = testHolderKlass(); + int c2HolderArray = testArray(); + int c2HolderGetter = testGetter(true, 0); + + Asserts.assertEQ(intHolderKlass, c2HolderKlass, "unexpected value from holder class:"); + Asserts.assertEQ(intHolderArray, c2HolderArray, "unexpected value from array:"); + Asserts.assertEQ(intHolderGetter, c2HolderGetter, "unexpected value from getter:"); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/OrINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/OrINodeIdealizationTests.java index b0eb1226c6b1..16b4c7d48cf9 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/OrINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/OrINodeIdealizationTests.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 @@ -27,7 +27,7 @@ /* * @test - * @bug 8322077 8353359 + * @bug 8322077 8353359 8373730 * @summary Test that Ideal transformations of OrINode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.OrINodeIdealizationTests @@ -38,7 +38,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1", "test2", "test3" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); int b = RunInfo.getRandom().nextInt(); @@ -57,6 +57,12 @@ public void assertResult(int a, int b) { Asserts.assertEQ((~a) | (~b), test1(a, b)); Asserts.assertEQ((a | 3) | 6, test2(a)); Asserts.assertEQ((a | 3) | a, test3(a)); + Asserts.assertEQ(a | (b | a), test4(a, b)); + Asserts.assertEQ((a | b) | a, test5(a, b)); + Asserts.assertEQ((a | 0) | a, test6(a)); + Asserts.assertEQ(a | (a | 0), test7(a)); + Asserts.assertEQ(a | b, test8(a, b)); + Asserts.assertEQ(a | b, test9(a, b)); } // Checks (~a) | (~b) => ~(a & b) @@ -81,4 +87,52 @@ public int test2(int a) { public int test3(int a) { return (a | 3) | a; } + + // Checks a | (b | a) => a | b + @Test + @IR(counts = { IRNode.OR, "1" }) + public int test4(int a, int b) { + return a | (b | a); + } + + // Checks (a | b) | a => a | b + @Test + @IR(counts = { IRNode.OR, "1" }) + public int test5(int a, int b) { + return (a | b) | a; + } + + // Checks (a | 0) | a => a | a => a + @Test + @IR(failOn = { IRNode.OR }) + public int test6(int a) { + return (a | 0) | a; + } + + // Checks a | (a | 0) => a | a => a + @Test + @IR(failOn = { IRNode.OR }) + public int test7(int a) { + return a | (a | 0); + } + + // The outer OR must be revisited when a * i becomes a, even if the inner OR remains. + // Declare b first so this replacement does not also cause the inner OR to swap its inputs. + @Test + @IR(counts = { IRNode.OR, "1" }, phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public int test8(int b, int a) { + int i; + for (i = -10; i < 1; i++) { + } + return a | (b | (a * i)); + } + + @Test + @IR(counts = { IRNode.OR, "1" }, phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public int test9(int b, int a) { + int i; + for (i = -10; i < 1; i++) { + } + return ((a * i) | b) | a; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/OrLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/OrLNodeIdealizationTests.java index 023e5fcc3d11..3f3eee8acd8a 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/OrLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/OrLNodeIdealizationTests.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 @@ -27,7 +27,7 @@ /* * @test - * @bug 8322077 8353359 + * @bug 8322077 8353359 8373730 * @summary Test that Ideal transformations of OrLNode* are being performed as expected. * @library /test/lib / * @run driver compiler.c2.irTests.OrLNodeIdealizationTests @@ -38,7 +38,7 @@ public static void main(String[] args) { TestFramework.run(); } - @Run(test = { "test1", "test2", "test3" }) + @Run(test = { "test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9" }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); @@ -57,6 +57,12 @@ public void assertResult(long a, long b) { Asserts.assertEQ((~a) | (~b), test1(a, b)); Asserts.assertEQ((a | 3) | 6, test2(a)); Asserts.assertEQ((a | 3) | a, test3(a)); + Asserts.assertEQ(a | (b | a), test4(a, b)); + Asserts.assertEQ((a | b) | a, test5(a, b)); + Asserts.assertEQ((a | 0L) | a, test6(a)); + Asserts.assertEQ(a | (a | 0L), test7(a)); + Asserts.assertEQ(a | b, test8(a, b)); + Asserts.assertEQ(a | b, test9(a, b)); } // Checks (~a) | (~b) => ~(a & b) @@ -82,4 +88,52 @@ public long test2(long a) { public long test3(long a) { return (a | 3) | a; } + + // Checks a | (b | a) => a | b + @Test + @IR(counts = { IRNode.OR, "1" }) + public long test4(long a, long b) { + return a | (b | a); + } + + // Checks (a | b) | a => a | b + @Test + @IR(counts = { IRNode.OR, "1" }) + public long test5(long a, long b) { + return (a | b) | a; + } + + // Checks (a | 0) | a => a | a => a + @Test + @IR(failOn = { IRNode.OR }) + public long test6(long a) { + return (a | 0L) | a; + } + + // Checks a | (a | 0) => a | a => a + @Test + @IR(failOn = { IRNode.OR }) + public long test7(long a) { + return a | (a | 0L); + } + + // The outer OR must be revisited when a * i becomes a, even if the inner OR remains. + // Declare b first so this replacement does not also cause the inner OR to swap its inputs. + @Test + @IR(counts = { IRNode.OR, "1" }, phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public long test8(long b, long a) { + int i; + for (i = -10; i < 1; i++) { + } + return a | (b | (a * i)); + } + + @Test + @IR(counts = { IRNode.OR, "1" }, phase = CompilePhase.BEFORE_MACRO_EXPANSION) + public long test9(long b, long a) { + int i; + for (i = -10; i < 1; i++) { + } + return ((a * i) | b) | a; + } } diff --git a/test/hotspot/jtreg/compiler/hotcode/TestHotCodeHeapOptions.java b/test/hotspot/jtreg/compiler/hotcode/TestHotCodeHeapOptions.java new file mode 100644 index 000000000000..3e640433a736 --- /dev/null +++ b/test/hotspot/jtreg/compiler/hotcode/TestHotCodeHeapOptions.java @@ -0,0 +1,241 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test TestHotCodeHeapOptions + * @bug 8389652 + * @summary Checks VM options related to the hot code heap + * @library /test/lib + * @requires vm.compiler1.enabled & vm.compiler2.enabled & vm.flagless + * + * @run driver compiler.hotcode.TestHotCodeHeapOptions + */ + +package compiler.hotcode; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestHotCodeHeapOptions { + private static void passes(ProcessBuilder pb) throws Exception { + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldNotContain("HotCodeHeap disabled"); + out.shouldHaveExitValue(0); + + long actualHotCodeHeapSize = Long.parseLong(out.firstMatch("HotCodeHeapSize\\s+=\\s+(\\d+)", 1)); + if (actualHotCodeHeapSize == 0) { + throw new RuntimeException("HotCodeHeapSize should be non zero but is " + actualHotCodeHeapSize); + } + } + + private static void warnsWith(ProcessBuilder pb, String message) throws Exception { + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldContain(message); + out.shouldHaveExitValue(0); + + long actualHotCodeHeapSize = Long.parseLong(out.firstMatch("HotCodeHeapSize\\s+=\\s+(\\d+)", 1)); + if (actualHotCodeHeapSize != 0) { + throw new RuntimeException("HotCodeHeapSize should have been disabled and set to zero but is " + actualHotCodeHeapSize); + } + + String hotCodeHeap = out.firstMatch("HotCodeHeap\\s+=\\s+(\\w+)", 1); + if (!hotCodeHeap.equals("false")) { + throw new RuntimeException("HotCodeHeap should be false but is " + hotCodeHeap); + } + } + + private static void failsWith(ProcessBuilder pb, String message) throws Exception { + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldContain(message); + out.shouldHaveExitValue(1); + } + + /** + * Check the result of hot code heap related VM options. + */ + public static void main(String[] args) throws Exception { + ProcessBuilder pb; + + // No additional flags + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-version"); + passes(pb); + + // Valid sampling periods + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=100", + "-XX:HotCodeMaxSamplingMs=100", + "-version"); + passes(pb); + + // Valid sampling periods + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=0", + "-XX:HotCodeMaxSamplingMs=1", + "-version"); + passes(pb); + + // Valid sampling periods (HotCodeMinSamplingMs = HotCodeMaxSamplingMs) + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=1", + "-XX:HotCodeMaxSamplingMs=1", + "-version"); + passes(pb); + + // Invalid sampling periods + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=1000", + "-XX:HotCodeMaxSamplingMs=100", + "-version"); + failsWith(pb, "HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs."); + + // Invalid sampling periods when HotCodeHeap is ergonomically disabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=1000", + "-XX:HotCodeMaxSamplingMs=100", + "-XX:-SegmentedCodeCache", + "-version"); + failsWith(pb, "HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs."); + + // Invalid sampling periods when HotCodeHeap is enabled (HotCodeMaxSamplingMs=0) + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeMinSamplingMs=0", + "-XX:HotCodeMaxSamplingMs=0", + "-version"); + failsWith(pb, "Improperly specified VM option"); + + // SegmentedCodeCache enabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:+SegmentedCodeCache", + "-version"); + passes(pb); + + // SegmentedCodeCache disabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-SegmentedCodeCache", + "-version"); + warnsWith(pb, "HotCodeHeap disabled and HotCodeHeapSize zeroed because SegmentedCodeCache is disabled."); + + // SegmentedCodeCache disabled with HotCodeHeapSize explicitly set + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-SegmentedCodeCache", + "-XX:HotCodeHeapSize=8m", + "-version"); + warnsWith(pb, "HotCodeHeap disabled and HotCodeHeapSize zeroed because SegmentedCodeCache is disabled."); + + // TieredCompilation enabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:+TieredCompilation", + "-version"); + passes(pb); + + // TieredCompilation disabled with default code cache + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-TieredCompilation", + "-version"); + passes(pb); + + // TieredCompilation disabled with large code cache + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-TieredCompilation", + "-XX:ReservedCodeCacheSize=240m", + "-version"); + passes(pb); + + // TieredCompilation disabled with small code cache + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-TieredCompilation", + "-XX:ReservedCodeCacheSize=120m", + "-version"); + passes(pb); + + // C2 disabled with HotCodeHeapSize explicitly set + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:TieredStopAtLevel=1", + "-XX:HotCodeHeapSize=8m", + "-version"); + warnsWith(pb, "HotCodeHeap disabled and HotCodeHeapSize zeroed because C2 is disabled."); + + // NMethodRelocation enabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:+NMethodRelocation", + "-version"); + passes(pb); + + // NMethodRelocation disabled + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:-NMethodRelocation", + "-version"); + warnsWith(pb, "HotCodeHeap disabled and HotCodeHeapSize zeroed because NMethodRelocation is disabled."); + + // HotCodeHeapSize set without HotCodeHeap + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:HotCodeHeapSize=8m", + "-version"); + failsWith(pb, "HotCodeHeapSize requires HotCodeHeap enabled."); + + // HotCodeHeapSize set to zero + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+PrintFlagsFinal", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+HotCodeHeap", + "-XX:HotCodeHeapSize=0", + "-version"); + failsWith(pb, "Not enough space in hot code heap to run VM"); + } +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java b/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java index aef01d20567d..78bbff9d3ded 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestArraysHashCode.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 @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=normal * @bug 8301093 * @summary Verify failure to intrinsify does not pollute control flow * @modules java.base/jdk.internal.util:+open @@ -30,6 +30,15 @@ * @run main/othervm -Xbatch -XX:-TieredCompilation compiler.intrinsics.TestArraysHashCode */ +/* + * @test id=osr + * @bug 8391146 + * @summary Verify array type check in OSR compilation + * @modules java.base/jdk.internal.util:+open + * + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=50 compiler.intrinsics.TestArraysHashCode + */ + package compiler.intrinsics; import java.lang.reflect.Field; diff --git a/test/hotspot/jtreg/compiler/loopopts/TestStartDominator.java b/test/hotspot/jtreg/compiler/loopopts/TestStartDominator.java new file mode 100644 index 000000000000..a614afb6444f --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestStartDominator.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8391777 + * @summary Test detection of start node when browsing dominator nodes to narrow IV type + * @requires vm.compiler2.enabled + * @run main/othervm -Xcomp -XX:-TieredCompilation + * -XX:CompileCommand=quiet -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +package compiler.loopopts; + +public class TestStartDominator { + static float getShort() { + return 42; + } + + static short test() { + short value = (short) -getShort(); + + while (-1 > ++value) { } + return value; + } + + public static void main(String[] args) { + test(); + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java index 704827d55653..e3c4957c6be0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java @@ -44,7 +44,7 @@ import jdk.test.whitebox.WhiteBox; import java.lang.reflect.Method; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CompletableFuture; public class CorrectlyRestoreRfp { static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -112,7 +112,7 @@ public static LargeValueWithOops compile_me_C1_testLargeValueWithOopsHelper(Obje static class GarbageProducerThread extends Thread { public void run() { - for (;;) { + while (!isInterrupted()) { // Produce some garbage and then let the GC do its work Object[] arrays = new Object[1024]; for (int i = 0; i < arrays.length; i++) { @@ -135,22 +135,18 @@ public static void main(String[] args) throws Exception { garbage_producer.setDaemon(true); garbage_producer.start(); - CountDownLatch cdl = new CountDownLatch(1); - Thread.ofPlatform().start(() -> { - try { + try { + CompletableFuture.runAsync(() -> { // Trigger compilation for (int i = 0; i < 500_000; i++) { Object val = new SmallValue(i); var v = compile_me_C1_testLargeValueWithOopsHelper(val); LargeValueWithOops.compile_me_C2_verify(v, "return", val, false); } - cdl.countDown(); - } catch (Exception e) { - System.out.println("Exception thrown: " + e); - e.printStackTrace(System.out); - System.exit(1); - } - }); - cdl.await(); + }, Thread.ofPlatform()::start).join(); + } finally { + garbage_producer.interrupt(); + garbage_producer.join(); + } } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java index a7f829410d35..116f5b0fc82c 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrays.java @@ -844,6 +844,98 @@ public void test29_verifier() { Asserts.assertEQ(src[0], v); } + // Check that scalar replacemnt is not applied when cloning an atomic array (JDK-8390513) + // Test29a performs a clone() of an atomic array just before a safepoint. + // IR verification looks for the copying of the content to the new array. + // If scalar replacement is applied, no array is copied, elements' fields + // are just loaded with LOAD_* nodes in case SafePointScalarObject nodes need them + static value class MyValueOfBytes { + byte b0, b1, b2, b3, b4, b5; + MyValueOfBytes() { + this((byte)0); + } + MyValueOfBytes(int i) { + byte b = (byte)i; + b0 = b; + b1 = b; + b2 = b; + b3 = b; + b4 = b; + b5 = b; + } + } + + static final MyValueOfBytes[] mvobArray29a; + static { + mvobArray29a = new MyValueOfBytes[64]; + for (int i = 0; i < mvobArray29a.length; i++) { + mvobArray29a[i] = new MyValueOfBytes(i); + } + } + + @Test + @IR(applyIf = {"UseArrayFlattening", "true"}, + counts = {JLONG_ARRAYCOPY, "= 1"}, + phase = CompilePhase.AFTER_MACRO_EXPANSION) + public int test29a(boolean check) { + MyValueOfBytes[] array = mvobArray29a.clone(); + return check ? test29a_consumer(array) : 0; + } + + @DontInline + public int test29a_consumer(MyValueOfBytes[] array) { + int ret = 0; + for (int i = 0; i < array.length; i++) { + var val = array[i]; + ret += val.b0 + val.b1 + val.b2 + val.b3 + val.b4 + val.b5; + } + return ret; + } + + @Run(test = "test29a") + public void test29a_verifier() { + int v; + for (int i = 0; i < 10_000; i++) { + v = test29a(false); + } + } + + static final MyValueOfBytes[] mvobArray29b; + static { + mvobArray29b = (MyValueOfBytes[])ValueClass.newNullRestrictedAtomicArray(MyValueOfBytes.class, 64, new MyValueOfBytes(0)); + for (int i = 0; i < mvobArray29b.length; i++) { + mvobArray29b[i] = new MyValueOfBytes(i); + } + } + + // Same as test29a but with a null-free atomic array + @Test + @IR(applyIf = {"UseArrayFlattening", "true"}, + counts = {JLONG_ARRAYCOPY, "= 1"}, + phase = CompilePhase.AFTER_MACRO_EXPANSION) + public int test29b(boolean check) { + MyValueOfBytes[] array = mvobArray29b.clone(); + return check ? test29a_consumer(array) : 0; + } + + @DontInline + public int test29b_consumer(MyValueOfBytes[] array) { + int ret = 0; + for (int i = 0; i < array.length; i++) { + var val = array[i]; + ret += val.b0 + val.b1 + val.b2 + val.b3 + val.b4 + val.b5; + } + return ret; + } + + @Run(test = "test29b") + public void test29b_verifier() { + int v; + for (int i = 0; i < 10_000; i++) { + v = test29b(false); + } + } + // non escaping allocation with uncommon trap that needs // eliminated value class array element as debug info @Test diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAtomicFlatArrayClone.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAtomicFlatArrayClone.java new file mode 100644 index 000000000000..a7f77919d8b9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestAtomicFlatArrayClone.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8390513 + * @summary Test that scalar replacement of a flat array clone preserves atomicity of loads + * @requires vm.compiler2.enabled + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main/othervm -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::clone + * -XX:CompileCommand=compileonly,${test.main.class}::write + * -XX:CompileCommand=dontinline,${test.main.class}::verify + * ${test.main.class} N_ARRAY + */ + +/* + * @test + * @bug 8390513 + * @summary Test that scalar replacement of a flat array clone preserves atomicity of loads + * @requires vm.compiler2.enabled + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main/othervm -XX:-TieredCompilation -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::clone + * -XX:CompileCommand=compileonly,${test.main.class}::write + * -XX:CompileCommand=dontinline,${test.main.class}::verify + * ${test.main.class} NF_ARRAY + */ +package compiler.valhalla.valuetypes; + +import java.util.Properties; +import jdk.internal.value.ValueClass; + +public class TestAtomicFlatArrayClone { + static value class MyValue { + final byte a; + final byte b; + final byte c; + final byte d; + final byte e; + final byte f; + final byte g; + + MyValue(byte value) { + a = value; + b = value; + c = value; + d = value; + e = value; + f = value; + g = value; + } + + boolean isConsistent() { + return a == b && a == c && a == d && a == e && a == f && a == g; + } + } + + static String array_kind; + + // Secondary class to hold the reference to the array because the array needs to be referenced + // from a static final field to observe the element corruption, and we need this array to be + // configured from command line arguments + static final class ArrayHolder { + static final MyValue ONE = new MyValue((byte)1); + static final MyValue TWO = new MyValue((byte)2); + static final MyValue[] ARRAY; + static { + switch(TestAtomicFlatArrayClone.array_kind) { + case "N_ARRAY" : ARRAY = new MyValue[64]; + break; + case "NF_ARRAY" : ARRAY = (MyValue[])ValueClass.newNullRestrictedAtomicArray(MyValue.class, 64, new MyValue((byte)0)); + break; + default: + throw new RuntimeException("Invalid argument for test configuration"); + } + for (int i = 0; i < ARRAY.length; ++i) { + ARRAY[i] = ONE; + } + } + } + + static volatile boolean stop; + + static void clone(boolean deoptimize) { + MyValue[] copy = ArrayHolder.ARRAY.clone(); + if (deoptimize) { + verify(copy); + } + } + + static void write(MyValue value) { + for (int i = 0; i < ArrayHolder.ARRAY.length; i++) { + ArrayHolder.ARRAY[i] = value; + } + } + + static void verify(MyValue[] copy) { + for (int i = 0; i < copy.length; i++) { + MyValue value = copy[i]; + if (!value.isConsistent()) { + throw new RuntimeException("Torn value at " + i + ": " + + value.a + ", " + value.b + ", " + value.c + ", " + value.d + ", " + + value.e + ", " + value.f + ", " + value.g); + } + } + } + + public static void main(String[] args) throws Exception { + array_kind = args[0]; + // Warmup + for (int i = 0; i < 20_000; i++) { + clone(false); + write(ArrayHolder.ONE); + } + + // Spawn a few threads that write to the array concurrently + Thread[] writers = new Thread[4]; + for (int i = 0; i < writers.length; i++) { + writers[i] = new Thread(() -> { + while (!stop) { + write(ArrayHolder.ONE); + write(ArrayHolder.TWO); + } + }); + writers[i].setDaemon(true); + writers[i].start(); + } + + // Now clone the array and verify the value elements are consistent + try { + clone(true); + } finally { + stop = true; + for (Thread writer : writers) { + writer.join(); + } + } + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestExpensiveWithStoreFlatCtrl.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestExpensiveWithStoreFlatCtrl.java new file mode 100644 index 000000000000..b51948fb83c0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestExpensiveWithStoreFlatCtrl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8391669 + * @summary Test expensive node with StoreFlat control input. + * @enablePreview + * @library /test/lib + * @run main ${test.main.class} + * @run main/othervm -Xcomp -XX:CompileCommand=quiet + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +package compiler.loopopts; + +import jdk.test.lib.Asserts; + +public class TestExpensiveWithStoreFlatCtrl { + static double test() { + // Trigger loop opts + for (int i = 0; i < 1; i++) { + } + + Integer[] array = new Integer[1]; + // Expensive SqrtDNode + double res = Math.sqrt(-1.0); + // StoreFlat + array[0] = 43; + // Equivalent SqrtDNode to prevent cleanup from removing control + res += Math.sqrt(-1.0); + return res; + } + + public static void main(String[] args) { + Math.sqrt(4.0); // Make sure it's loaded + Asserts.assertTrue(Double.isNaN(test())); + } +} + diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOopsInReturnConvention.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOopsInReturnConvention.java index 934166f366d8..1cfd9be98d24 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOopsInReturnConvention.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestOopsInReturnConvention.java @@ -215,7 +215,7 @@ public static void caller(LargeValueWithOops val, Object obj, boolean useNull) { static class GarbageProducerThread extends Thread { public void run() { - for (;;) { + while (!isInterrupted()) { // Produce some garbage and then let the GC do its work Object[] arrays = new Object[1024]; for (int i = 0; i < arrays.length; i++) { @@ -238,11 +238,16 @@ public static void main(String[] args) throws Exception { garbage_producer.setDaemon(true); garbage_producer.start(); - // Trigger compilation - for (int i = 0; i < 100_000; i++) { - boolean useNull = (i % 2) == 0; - LargeValueWithOops val = useNull ? null : new LargeValueWithOops(i); - caller(val, i, useNull); + try { + // Trigger compilation + for (int i = 0; i < 100_000; i++) { + boolean useNull = (i % 2) == 0; + LargeValueWithOops val = useNull ? null : new LargeValueWithOops(i); + caller(val, i, useNull); + } + } finally { + garbage_producer.interrupt(); + garbage_producer.join(); } } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReinterpretValueAtSafepoint.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReinterpretValueAtSafepoint.java new file mode 100644 index 000000000000..8cd3d9960b82 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReinterpretValueAtSafepoint.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test type verification of writes of scalarized object at safepoint + * @bug 8389342 + * @requires vm.compiler2.enabled + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main/othervm -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:RepeatCompilation=100 + * -XX:+StressEliminateAllocations -XX:CompileOnly=${test.main.class}::test + * ${test.main.class} + */ + +package compiler.valhalla.valuetypes; + +public class TestReinterpretValueAtSafepoint { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(i); + } + } + + static V1 test(int i) { + return new V1(i); + } + + static value class V1 { + double d; + V2 v2; + + V1(int i) { + // Folding of a reinterpret cast into memory operation would + // transform the StoreD into a StoreL + d = Double.longBitsToDouble(i); + v2 = i == -1 ? null : new V2(); + } + } + + static value class V2 {} +} diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java index a38eef79b79b..a9b4dbfaac2c 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java @@ -225,8 +225,8 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collections; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.LockSupport; -import java.util.concurrent.CountDownLatch; import java.util.Random; import jdk.test.lib.Platform; @@ -746,7 +746,7 @@ public static DoubleValue2 testDoubleValue2Helper(double d, boolean useNull, boo static class GarbageProducerThread extends Thread { public void run() { - for (;;) { + while (!isInterrupted()) { // Produce some garbage and then let the GC do its work Object[] arrays = new Object[1024]; for (int i = 0; i < arrays.length; i++) { @@ -757,36 +757,29 @@ public void run() { } } - public static void startTest(CountDownLatch cdl, Thread.Builder builder, int iterations) { - builder.start(() -> { - try { - // Trigger compilation - boolean isVirtual = Thread.currentThread().isVirtual(); - for (int i = 0; i < iterations; i++) { - boolean park = (i % 1000) == 0; - boolean useNull = RAND.nextBoolean(); - Object val = useNull ? null : new SmallValue(i); - SmallValue.verify(testSmallHelper(i, useNull, park), "return", i, useNull); - LargeValue.verify(testLargeHelper(i, useNull, park), "return", i, useNull); - LargeValue.verify(testLargeManyArgsHelper(i, useNull, park), "return", i + 3, useNull); - LargeValue2.verify(testLarge2Helper(i, useNull, park), "return", i, useNull); - LargeValue2.verify(testLarge2ManyArgsHelper(i, useNull, park), "return", i + 3, useNull); - testExtendsAbstractHelper(i, park).verify("return", i + 3); - LargeValueWithOops.verify(testLargeValueWithOopsHelper(val, useNull, park), "return", val, useNull); - LargeValueWithOops.verify(testLargeValueWithOops2Helper(val, useNull, park), "return", val, useNull); - DoubleValue.verify(testDoubleValueHelper(i, useNull, park), "return", i, useNull); - DoubleValue2.verify(testDoubleValue2Helper(i, useNull, park), "return", i, useNull); - if (i % 1000 == 0) { - System.out.format("%s => %s %d of %d%n", Instant.now(), isVirtual ? "Virtual: " : "Platform:", i, iterations); - } + public static CompletableFuture startTest(Thread.Builder builder, int iterations) { + return CompletableFuture.runAsync(() -> { + // Trigger compilation + boolean isVirtual = Thread.currentThread().isVirtual(); + for (int i = 0; i < iterations; i++) { + boolean park = (i % 1000) == 0; + boolean useNull = RAND.nextBoolean(); + Object val = useNull ? null : new SmallValue(i); + SmallValue.verify(testSmallHelper(i, useNull, park), "return", i, useNull); + LargeValue.verify(testLargeHelper(i, useNull, park), "return", i, useNull); + LargeValue.verify(testLargeManyArgsHelper(i, useNull, park), "return", i + 3, useNull); + LargeValue2.verify(testLarge2Helper(i, useNull, park), "return", i, useNull); + LargeValue2.verify(testLarge2ManyArgsHelper(i, useNull, park), "return", i + 3, useNull); + testExtendsAbstractHelper(i, park).verify("return", i + 3); + LargeValueWithOops.verify(testLargeValueWithOopsHelper(val, useNull, park), "return", val, useNull); + LargeValueWithOops.verify(testLargeValueWithOops2Helper(val, useNull, park), "return", val, useNull); + DoubleValue.verify(testDoubleValueHelper(i, useNull, park), "return", i, useNull); + DoubleValue2.verify(testDoubleValue2Helper(i, useNull, park), "return", i, useNull); + if (i % 1000 == 0) { + System.out.format("%s => %s %d of %d%n", Instant.now(), isVirtual ? "Virtual: " : "Platform:", i, iterations); } - cdl.countDown(); - } catch (Exception e) { - System.out.println("Exception thrown: " + e); - e.printStackTrace(System.out); - System.exit(1); } - }); + }, task -> builder.start(task)); } public static void main(String[] args) throws Exception { @@ -821,16 +814,18 @@ public static void main(String[] args) throws Exception { garbage_producer.setDaemon(true); garbage_producer.start(); - int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 300_000; - if (Platform.isDebugBuild()) { - iterations /= 4; + try { + int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 300_000; + if (Platform.isDebugBuild()) { + iterations /= 4; + } + CompletableFuture platform = startTest(Thread.ofPlatform(), iterations); + CompletableFuture virtual = startTest(Thread.ofVirtual(), iterations); + CompletableFuture.allOf(platform, virtual).join(); + } finally { + garbage_producer.interrupt(); + garbage_producer.join(); } - CountDownLatch cdlPlatform = new CountDownLatch(1); - CountDownLatch cdlVirtual = new CountDownLatch(1); - startTest(cdlPlatform, Thread.ofPlatform(), iterations); - startTest(cdlVirtual, Thread.ofVirtual(), iterations); - cdlPlatform.await(); - cdlVirtual.await(); } } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorFallbackInlining.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorFallbackInlining.java index dc332d56970e..ae8b759eb404 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorFallbackInlining.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorFallbackInlining.java @@ -78,12 +78,17 @@ public class TestVectorFallbackInlining { "-XX:CompileCommand=inline,jdk.internal.vm.vector.VectorSupport::insert"; public static void main(String[] args) { + // Note: Some platforms don't activate EnableVectorSupport by default. Required here. TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", FORCE_INLINE_FALLBACK, - "-XX:+IncrementalInlineVector"); + "-XX:+IncrementalInlineVector", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+EnableVectorSupport"); TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", FORCE_INLINE_FALLBACK, - "-XX:-IncrementalInlineVector"); + "-XX:-IncrementalInlineVector", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+EnableVectorSupport"); } @Test diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLogicConeFuzzer.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLogicConeFuzzer.java index f600b470e9a7..a888767c47fc 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLogicConeFuzzer.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLogicConeFuzzer.java @@ -218,13 +218,9 @@ public static String generate(CompileFramework comp) { // MacroLogicV IR matching is only asserted for non-masked cones; masked // cones may not pack into MacroLogicV (e.g. mixed masks or partial predication). - Object testMethodHeader = numMasks == 0 - ? """ - @IR(applyIf = {"UseAVX", "3"}, counts = {IRNode.MACRO_LOGIC_V, " > 0 "}) - @Test - public static Object $test( + // TODO 8392367: Add IR verification + Object testMethodHeader = """ - : """ @Test public static Object $test( """; diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java index a6128682ab11..f895283160a1 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorMaskCompareNotTest.java @@ -35,7 +35,7 @@ * @library /test/lib / * @summary test combining vector not operation with compare * @modules jdk.incubator.vector - * @requires vm.opt.final.MaxVectorSize == "null" | vm.opt.final.MaxVectorSize >= 16 + * @requires vm.opt.final.MaxVectorSize == null | vm.opt.final.MaxVectorSize >= 16 * * @run driver compiler.vectorapi.VectorMaskCompareNotTest */ diff --git a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorReinterpret.java b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorReinterpret.java index 400809383fc2..831536a157af 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorReinterpret.java +++ b/test/hotspot/jtreg/compiler/vectorapi/reshape/TestVectorReinterpret.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 @@ -39,7 +39,7 @@ * @modules jdk.incubator.vector * @modules java.base/jdk.internal.misc * @summary Test that vector reinterpret intrinsics work as intended. - * @requires os.arch != "riscv64" | vm.cpu.features ~= ".*rvv.*" + * @requires vm.opt.final.MaxVectorSize == null | vm.opt.final.MaxVectorSize >= 16 * @library /test/lib / * @run main compiler.vectorapi.reshape.TestVectorReinterpret */ diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorsNotSavedAtSafepoint.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorsNotSavedAtSafepoint.java index aa92f5927ce2..9c8eadb63d79 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestVectorsNotSavedAtSafepoint.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorsNotSavedAtSafepoint.java @@ -58,7 +58,7 @@ public static void test2(int[] iArr, long[] lArr) { static class GarbageProducerThread extends Thread { public void run() { - for(;;) { + while (!isInterrupted()) { // Produce some garbage and then let the GC do its work which will // corrupt vector registers if they are not saved at safepoints. Object[] arrays = new Object[1024]; @@ -70,33 +70,38 @@ public void run() { } } - public static void main(String[] args) { + public static void main(String[] args) throws InterruptedException { Thread garbage_producer = new GarbageProducerThread(); garbage_producer.setDaemon(true); garbage_producer.start(); - if (args[0].equals("test1")) { - byte[] bArr = new byte[10]; - long[] lArr = new long[1000]; - for (int i = 0; i < 10_000; ++i) { - test1(bArr, bArr, bArr, lArr, -1); - for (int j = 0; j < lArr.length; ++j) { - if (bArr[j % 10] != 0 || lArr[j] != -1) { - throw new RuntimeException("Test1 failed at iteration " + i + ": bArr[" + (j % 10) + "] = " + bArr[j % 10] + ", lArr[" + j + "] = " + lArr[j]); + try { + if (args[0].equals("test1")) { + byte[] bArr = new byte[10]; + long[] lArr = new long[1000]; + for (int i = 0; i < 10_000; ++i) { + test1(bArr, bArr, bArr, lArr, -1); + for (int j = 0; j < lArr.length; ++j) { + if (bArr[j % 10] != 0 || lArr[j] != -1) { + throw new RuntimeException("Test1 failed at iteration " + i + ": bArr[" + (j % 10) + "] = " + bArr[j % 10] + ", lArr[" + j + "] = " + lArr[j]); + } } } - } - } else { - int iArr[] = new int[100]; - long lArr[] = new long[100]; - for (int i = 0; i < 10_000; ++i) { - test2(iArr, lArr); - for (int j = 0; j < lArr.length; ++j) { - if (iArr[j] != 1 || lArr[j] != 1) { - throw new RuntimeException("Test2 failed at iteration " + i + ": iArr[" + j + "] = " + iArr[j] + ", lArr[" + j + "] = " + lArr[j]); + } else { + int iArr[] = new int[100]; + long lArr[] = new long[100]; + for (int i = 0; i < 10_000; ++i) { + test2(iArr, lArr); + for (int j = 0; j < lArr.length; ++j) { + if (iArr[j] != 1 || lArr[j] != 1) { + throw new RuntimeException("Test2 failed at iteration " + i + ": iArr[" + j + "] = " + iArr[j] + ", lArr[" + j + "] = " + lArr[j]); + } } } } + } finally { + garbage_producer.interrupt(); + garbage_producer.join(); } } } diff --git a/test/hotspot/jtreg/containers/docker/TestPids.java b/test/hotspot/jtreg/containers/docker/TestPids.java index 2bc5f8c03c21..acb148c1bc02 100644 --- a/test/hotspot/jtreg/containers/docker/TestPids.java +++ b/test/hotspot/jtreg/containers/docker/TestPids.java @@ -42,15 +42,12 @@ import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.Asserts; -import jdk.test.lib.Container; import jdk.test.lib.Platform; import jdk.test.lib.Utils; public class TestPids { private static final String imageName = Common.imageName("pids"); - private static final boolean IS_PODMAN = DockerTestUtils.isPodman(); - private static final int UNLIMITED_PIDS_PODMAN = 0; - private static final int UNLIMITED_PIDS_DOCKER = -1; + private static final int UNLIMITED_PIDS = -1; static final String warning_kernel_no_pids_support = "WARNING: Your kernel does not support pids limit capabilities"; @@ -137,22 +134,21 @@ private static void checkResult(List lines, String lineMarker, String ex Asserts.assertTrue(lineMarkerFound); } - private static void testPids(String value) throws Exception { - Common.logNewTestCase("pids controller test, limiting value = " + value); + private static void testPids(String pidsLimit) throws Exception { + Common.logNewTestCase("pids controller test, limiting value = " + pidsLimit); DockerRunOptions opts = commonOpts(); - if (value.equals("Unlimited")) { - int unlimited = IS_PODMAN ? UNLIMITED_PIDS_PODMAN : UNLIMITED_PIDS_DOCKER; - opts.addDockerOpts("--pids-limit=" + unlimited); + if (pidsLimit.equals("Unlimited")) { + opts.addDockerOpts("--pids-limit=" + UNLIMITED_PIDS); } else { - opts.addDockerOpts("--pids-limit="+value); + opts.addDockerOpts("--pids-limit=" + pidsLimit); } List lines = Common.run(opts).asLines(); - if (value.equals("Unlimited")) { + if (pidsLimit.equals("Unlimited")) { checkResult(lines, "Maximum number of tasks is: ", "max"); } else { - checkResult(lines, "Maximum number of tasks is: ", value); + checkResult(lines, "Maximum number of tasks is: ", pidsLimit); } // current number of tasks value is hard to predict, so better expect no value checkResult(lines, "Current number of tasks is: ", "any_integer"); diff --git a/test/hotspot/jtreg/gc/arguments/TestG1HeapRegionSize.java b/test/hotspot/jtreg/gc/arguments/TestG1HeapRegionSize.java index 97b15bdc5c90..5084134f9453 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1HeapRegionSize.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1HeapRegionSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ /* * @test TestG1HeapRegionSize - * @bug 8021879 + * @bug 8021879 8311029 * @requires vm.gc.G1 & vm.opt.G1HeapRegionSize == null * @summary Verify that the flag G1HeapRegionSize is updated properly * @modules java.base/jdk.internal.misc @@ -78,6 +78,8 @@ public static void main(String args[]) throws Exception { final int M = 1024 * 1024; checkG1HeapRegionSize(new String[] { "-Xmx64m" /* default is 1m */ }, 1*M, 0); + checkG1HeapRegionSize(new String[] { "-Xmx64m", "-XX:G1HeapRegionSize=0" }, 1*M, 0); + checkG1HeapRegionSize(new String[] { "-Xmx64m", "-XX:G1HeapRegionSize=16" }, 0, 1); checkG1HeapRegionSize(new String[] { "-Xmx64m", "-XX:G1HeapRegionSize=2m" }, 2*M, 0); checkG1HeapRegionSize(new String[] { "-Xmx64m", "-XX:G1HeapRegionSize=3m" }, 4*M, 0); checkG1HeapRegionSize(new String[] { "-Xmx256m", "-XX:G1HeapRegionSize=32m" }, 32*M, 0); diff --git a/test/hotspot/jtreg/gc/shenandoah/generational/TestHumongousAllocFailureDegeneration.java b/test/hotspot/jtreg/gc/shenandoah/generational/TestHumongousAllocFailureDegeneration.java new file mode 100644 index 000000000000..29a0aedb3335 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/generational/TestHumongousAllocFailureDegeneration.java @@ -0,0 +1,112 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test id=generational + * @bug 8391591 + * @summary Humongous allocation failure triggers a degenerated cycle. + * @requires vm.gc.Shenandoah + * @library /test/lib + * @run main/othervm + * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC + * -XX:ShenandoahGCMode=generational -Xmx128m -Xms128m + * -XX:ShenandoahRegionSize=1m -XX:+AlwaysPreTouch + * -XX:ConcGCThreads=1 -XX:ParallelGCThreads=1 + * TestHumongousAllocFailureDegeneration + */ + +import com.sun.management.GarbageCollectionNotificationInfo; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; + +public class TestHumongousAllocFailureDegeneration { + private static final int MB = 1024 * 1024; + private static final int HUMONGOUS_SIZE_MB = 32; + private static final int ITERATIONS = 16; + private static Object sink; + + private static boolean isCollectorNotification(Notification n) { + return n.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION); + } + + private static void subscribeToCollectorNotifications(NotificationListener listener) { + for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) b).addNotificationListener(listener, null, null); + } + } + + private static void unsubscribeToCollectorNotifications(NotificationListener listener) throws Exception { + for (GarbageCollectorMXBean b : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) b).removeNotificationListener(listener, null, null); + } + } + + private static boolean isHumongousAllocFailureDegen(GarbageCollectionNotificationInfo info) { + return info.getGcName().equals("Shenandoah Pauses") + && info.getGcAction().contains("Degenerated") + && info.getGcCause().equals("Humongous Allocation Failure"); + } + + public static void main(String[] args) throws Exception { + final AtomicBoolean sawDegenForHumongous = new AtomicBoolean(false); + + NotificationListener listener = (Notification n, Object o) -> { + if (isCollectorNotification(n)) { + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); + if (isHumongousAllocFailureDegen(info)) { + sawDegenForHumongous.set(true); + } + } + }; + + subscribeToCollectorNotifications(listener); + + for (int i = 0; i < ITERATIONS; i++) { + // Requests 33 contiguous regions. We're likely to hit humongous allocation failure + // because the GC can't keep up with the allocations. + sink = new byte[MB * HUMONGOUS_SIZE_MB]; + } + + // Wait for gc notifications until we've encountered a degenerated cycle caused by + // humongous allocation failure. Fail if none arrives before the deadline. + long deadlineNanos = System.nanoTime() + TimeUnit.SECONDS.toNanos(60); + while (!sawDegenForHumongous.get()) { + if (System.nanoTime() > deadlineNanos) { + throw new RuntimeException("Timed out waiting for a degenerated cycle caused by " + + "humongous allocation failure"); + } + Thread.sleep(10); + } + + unsubscribeToCollectorNotifications(listener); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java index da994edeb27f..2dd9725295a4 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java @@ -25,6 +25,7 @@ /* * @test CommandLineFlagCombo * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * @comment This test explicitly chooses the type of GC to be used by sub-processes. It may conflict with the GC type set * via the -vmoptions command line option of JTREG. vm.gc==null will help the test case to discard the explicitly passed * vm options. diff --git a/test/hotspot/jtreg/runtime/cds/appcds/HelloValueClassTest.java b/test/hotspot/jtreg/runtime/cds/appcds/HelloValueClassTest.java index 0e3b1582e90b..493456460e87 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/HelloValueClassTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/HelloValueClassTest.java @@ -31,7 +31,7 @@ * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation * @compile test-classes/HelloValueClassApp.java - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello_inline.jar + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello_value.jar * HelloValueClassApp HelloValueClassApp$Point * HelloValueClassApp$Rectangle HelloValueClassApp$ValueRecord * @run main/othervm HelloValueClassTest @@ -41,7 +41,7 @@ import jdk.test.lib.process.OutputAnalyzer; public class HelloValueClassTest { public static void main(String[] args) throws Exception { - String appJar = ClassFileInstaller.getJarPath("hello_inline.jar"); + String appJar = ClassFileInstaller.getJarPath("hello_value.jar"); String mainClass = "HelloValueClassApp"; OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list(mainClass, diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ValueFieldExclusionTest.java b/test/hotspot/jtreg/runtime/cds/appcds/ValueFieldExclusionTest.java index 763a39150fa2..6aa7a63c6aff 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ValueFieldExclusionTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ValueFieldExclusionTest.java @@ -23,9 +23,9 @@ */ /* - * @test id=inline + * @test id=flattened * @bug 8384756 - * @summary A class should be excluded if the type of one of its inlined fields is excluded. + * @summary A class should be excluded if the type of one of its flattened fields is excluded. * @requires vm.cds * @library /test/lib * @modules java.base/jdk.internal.vm.annotation @@ -38,7 +38,7 @@ /* @test id=static * @bug 8384756 - * @summary A class should be excluded if the type of one of its inlined fields is excluded. + * @summary A class should be excluded if the type of one of its null-restricted static fields is excluded. * @requires vm.cds * @library /test/lib * @modules java.base/jdk.internal.vm.annotation diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/HeapFragmentationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/HeapFragmentationTest.java index baecace95490..c7f708ce72a5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/HeapFragmentationTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/HeapFragmentationTest.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,6 +27,7 @@ * @summary Relocate CDS archived regions to the top of the G1 heap * @bug 8214455 * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * @requires (sun.arch.data.model == "64" & os.maxMemory > 4g) * @requires vm.gc == "null" * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds diff --git a/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/BigArityTest.java b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/BigArityTest.java new file mode 100644 index 000000000000..0bbf510db8e7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/BigArityTest.java @@ -0,0 +1,48 @@ +/* + * 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=aot + * @summary Run the BigArityTest.java test in CDSAppTester::AOT workflow. + * @requires vm.cds & vm.compMode != "Xcomp" + * @requires vm.cds.supports.aot.class.linking + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @compile ../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../jdk/java/lang/invoke/BigArityTest.java + * ../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * @run junit/othervm/timeout=480 -Dcds.app.tester.workflow=AOT BigArityTest + */ +import org.junit.Test; + +public class BigArityTest { + @Test + public void test() throws Exception { + JDKMethodHandlesTestRunner.test(BigArityTest.class.getName(), false); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java index d9e1b15602ca..b3181e0a4ec2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/methodHandles/JDKMethodHandlesTestRunner.java @@ -47,6 +47,9 @@ public class JDKMethodHandlesTestRunner { private static final String testPackageName = "test.java.lang.invoke"; public static void test(String testClassName) throws Exception { + test(testClassName, true); + } + public static void test(String testClassName, boolean mainClassHasLambdas) throws Exception { String appJar = JarBuilder.build("MH", new File(classDir), null); String classList = testClassName + ".list"; String archiveName = testClassName + ".jsa"; @@ -69,7 +72,7 @@ public String classpath(RunMode runMode) { public String[] vmArgs(RunMode runMode) { if (runMode.isProductionRun()) { return new String[] { - "-Xlog:class+load,cds=debug", + "-Xlog:class+load,cds=debug,aot+load", verifyOpt, }; } else { @@ -90,7 +93,7 @@ public String[] appCommandLine(RunMode runMode) { @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { out.shouldHaveExitValue(0); - if (runMode.isProductionRun()) { + if (runMode.isProductionRun() && mainClassHasLambdas) { out.shouldMatch(".class.load.* test.java.lang.invoke." + testClassName + "[$][$]Lambda.*/0x.*source:.*shared.*objects.*file"); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java index f2ec33cdefe6..5a193bf2ce29 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java @@ -32,6 +32,7 @@ * @summary Test options that are incompatible with use of shared strings * Also test mismatch in oops encoding between dump time and run time * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * @comment This test explicitly chooses the type of GC to be used by sub-processes. It may conflict with the GC type set * via the -vmoptions command line option of JTREG. vm.gc==null will help the test case to discard the explicitly passed * vm options. @@ -48,6 +49,7 @@ /* * @test * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * @requires (vm.gc=="null") * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds @@ -60,6 +62,7 @@ /* * @test * @requires vm.cds.write.archived.java.heap + * @requires vm.gc.G1 * @requires (vm.gc=="null") * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/HashOverflowTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/HashOverflowTest.java index 430bc7f0a752..21bf3b9a8a52 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/HashOverflowTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/HashOverflowTest.java @@ -30,7 +30,7 @@ * @enablePreview * @requires vm.flagless * @compile HashOverflowTest.java - * @run main/othervm -Xint -Xss256K + * @run main/othervm -Xint -Xss384K * runtime.valhalla.valuetypes.HashOverflowTest */ diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbInspectWithValueObject.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspectWithValueObject.java new file mode 100644 index 000000000000..9f2e1da67e03 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspectWithValueObject.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.SA.SATestUtils; + +import jtreg.SkippedException; + +/** + * @test + * @bug 8390106 + * @requires vm.hasSA + * @requires vm.gc != "Z" + * @requires (os.arch != "riscv64" | !(vm.cpu.features ~= ".*qemu.*")) + * @library /test/lib + * + * @enablePreview + * @build jdk.test.whitebox.WhiteBox + * @modules java.base/jdk.internal.vm.annotation + * @build LingeredAppWithValueObject + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm/timeout=480 ClhsdbInspectWithValueObject + */ + +public class ClhsdbInspectWithValueObject { + + public static void main(String[] args) throws Exception { + SATestUtils.skipIfCannotAttach(); + + LingeredAppWithValueObject theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + + theApp = new LingeredAppWithValueObject(); + LingeredApp.startAppExactJvmOpts( + theApp, + "--enable-preview", + "-Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir"), + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:.", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UseEpsilonGC" // Use Epsilon GC to prevent object promotion + ); + + Properties addresses = new Properties(); + try (var in = Files.newInputStream(LingeredAppWithValueObject.ADDR_FILE_PATH)) { + addresses.load(in); + } + + var cmds = List.of( + "inspect " + addresses.getProperty("valObj"), + "inspect " + addresses.getProperty("nonNullValObj"), + "inspect " + addresses.getProperty("nonFlattenedValObj") + ); + + var expStrMap = Map.of( + cmds.get(0) /* valObj */ , List.of( + "a: 1", + "rec:", + "recA: 10", + "recB: 20", + "nullField: null" + ), + cmds.get(1) /* nonNullValObj */ , List.of( + "a: 2", + "rec:", + "recA: 30", + "recB: 40" + ), + cmds.get(2) /* nonFlattenedValObj */ , List.of( + "a: 100", + "obj: Oop for java/lang/Object" + ) + ); + test.run(theApp.getPid(), cmds, expStrMap, null); + } finally { + try { + LingeredApp.stopApp(theApp); + } catch (IOException e) { + if (theApp.getOutput().getStderr().contains("OutOfMemoryError")) { + throw new SkippedException("No memory to test - LingeredApp for this test requires Epsilon GC", e); + } + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithValueObject.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithValueObject.java new file mode 100644 index 000000000000..3015ad60d552 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithValueObject.java @@ -0,0 +1,108 @@ + +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Properties; + +import jdk.internal.vm.annotation.NullRestricted; + +import jdk.test.lib.Asserts; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.whitebox.WhiteBox; + + +public class LingeredAppWithValueObject extends LingeredApp { + + public static final Path ADDR_FILE_PATH = Path.of(System.getProperty("java.io.tmpdir"), "LingeredAppWithValueObject-addresses.properties"); + + public static value record Rec(byte recA, byte recB){}; + + public static value class ValueObj { + + public final byte a; + public final Rec rec; + public final Rec nullField; + + public ValueObj(byte a, byte recA, byte recB) { + this.a = a; + this.rec = new Rec(recA, recB); + this.nullField = null; + } + } + + public static value class NonNullValueObj { + + public final byte a; + public final Rec rec; + + public NonNullValueObj(byte a, byte recA, byte recB) { + this.a = a; + this.rec = new Rec(recA, recB); + } + } + + public static value class NonFlattenedValueObj { + + public final int a; + public final Object obj; + + public NonFlattenedValueObj(int a, Object obj) { + this.a = a; + this.obj = obj; + } + } + + private static ValueObj valObj; + + @NullRestricted + private static NonNullValueObj nonNullValObj; + + private static NonFlattenedValueObj nonFlattenedValObj; + + static { + valObj = new ValueObj((byte)1, (byte)10, (byte)20); + nonNullValObj = new NonNullValueObj((byte)2, (byte)30, (byte)40); + nonFlattenedValObj = new NonFlattenedValueObj(100, new Object()); + } + + public static void main(String[] args) { + WhiteBox wb = WhiteBox.getWhiteBox(); + Properties addresses = new Properties(); + addresses.setProperty("valObj", String.format("0x%x", wb.getObjectAddress(valObj))); + addresses.setProperty("nonNullValObj", String.format("0x%x", wb.getObjectAddress(nonNullValObj))); + addresses.setProperty("nonFlattenedValObj", String.format("0x%x", wb.getObjectAddress(nonFlattenedValObj))); + + try (var out = Files.newOutputStream(ADDR_FILE_PATH)) { + addresses.store(out, null); + ADDR_FILE_PATH.toFile().deleteOnExit(); + } catch (IOException e) { + Asserts.fail("Unexpected exception happened", e); + } + + LingeredApp.main(args); + } +} diff --git a/test/hotspot/jtreg/serviceability/threads/ThreadSnapshotRaceTest.java b/test/hotspot/jtreg/serviceability/threads/ThreadSnapshotRaceTest.java new file mode 100644 index 000000000000..e60ef3b496b4 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/threads/ThreadSnapshotRaceTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2026, Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +import static jdk.test.lib.Asserts.assertTrue; + +/* + * @test + * @bug 8392031 + * @summary Make sure that ThreadSnapshot::initialize does not crash JVM + * @requires vm.continuations + * @library /test/lib + * @modules java.management + * @run main/othervm -Djdk.virtualThreadScheduler.parallelism=8 ThreadSnapshotRaceTest + */ +public class ThreadSnapshotRaceTest { + + private static volatile boolean SHOULD_STOP = false; + + public static void main(String[] args) throws Exception { + + Thread producer = new Thread(() -> { + AtomicLong counter = new AtomicLong(); + while (!SHOULD_STOP) { + long c = counter.incrementAndGet(); + Thread.ofVirtual().name("vthread").start(() -> { + counter.decrementAndGet(); + }); + if (c >= 1_000_000) { + do { + try { + Thread.sleep(50); + } catch (Exception e) { + } + } while (!SHOULD_STOP && counter.get() > 0); + } + } + }); + producer.start(); + + Thread consumer = new Thread(() -> { + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + long[] ids = carrierIds(bean); + while (!SHOULD_STOP && ids.length == 0) { + ids = carrierIds(bean); + } + while (!SHOULD_STOP) { + ThreadInfo[] infos = bean.getThreadInfo(ids); + assertTrue(infos.length > 0); + } + }); + consumer.start(); + + Thread.sleep(10_000); + SHOULD_STOP = true; + consumer.join(); + producer.join(); + } + + static long[] carrierIds(ThreadMXBean bean) { + long[] all = bean.getAllThreadIds(); + long[] carriers = Arrays.stream(bean.getThreadInfo(all)) + .filter(Objects::nonNull) + .filter(ti -> ti.getThreadName().startsWith("ForkJoinPool-1-worker")) + .mapToLong(ThreadInfo::getThreadId) + .toArray(); + return carriers; + } +} diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index d6c6a3b2247a..c0420b9d8324 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -115,7 +115,6 @@ java/awt/event/MouseEvent/MouseClickTest/MouseClickTest.java 8168389 windows-all,macosx-all java/awt/event/KeyEvent/SwallowKeyEvents/SwallowKeyEvents.java 8224055 macosx-all -java/awt/Focus/FocusOwnerFrameOnClick/FocusOwnerFrameOnClick.java 8081489 generic-all java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java 6849364 generic-all java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java 6848406 generic-all java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java 6848407 generic-all @@ -208,12 +207,10 @@ java/awt/Window/AlwaysOnTop/AutoTestOnTop.java 6847593 linux-all java/awt/Window/GrabSequence/GrabSequence.java 6848409 macosx-all,linux-all java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java 8203371 linux-all java/awt/font/TextLayout/TextLayoutBounds.java 8169188 generic-all -java/awt/image/BufferedImage/ICMColorDataTest/ICMColorDataTest.java 8233028 generic-all java/awt/image/DrawImage/IncorrectAlphaSurface2SW.java 8056077 linux-all java/awt/print/Headless/HeadlessPrinterJob.java 8196088 windows-all sun/awt/datatransfer/SuplementaryCharactersTransferTest.java 8011371 generic-all sun/awt/shell/ShellFolderMemoryLeak.java 8197794 windows-all -sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java 8196102 generic-all sun/java2d/DirectX/RenderingToCachedGraphicsTest/RenderingToCachedGraphicsTest.java 8196180 windows-all,macosx-all sun/java2d/OpenGL/OpaqueDest.java#id1 8367574 macosx-all sun/java2d/OpenGL/ScaleParamsOOB.java#id0 8377908 linux-all @@ -245,19 +242,19 @@ java/awt/Modal/FileDialog/FileDialogTKModal4Test.java 8196430 generic-all java/awt/Modal/FileDialog/FileDialogTKModal5Test.java 8196430 generic-all java/awt/Modal/FileDialog/FileDialogTKModal6Test.java 8196430 generic-all java/awt/Modal/FileDialog/FileDialogTKModal7Test.java 8196430 macosx-all -java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java 8066259 macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeFrameFileTest.java 8047179 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogFileTest.java 8047179 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogPageSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogPrintSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeFramePageSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ApplicationExcludeFramePrintSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeFrameFileTest.java 8047179 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogFileTest.java 8047179 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogPageSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogPrintSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeFramePageSetupTest.java 8196431 linux-all,macosx-all -java/awt/Modal/ModalExclusionTests/ToolkitExcludeFramePrintSetupTest.java 8196431 linux-all,macosx-all +java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java 8392521 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeFrameFileTest.java 8047179 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogFileTest.java 8047179 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogPageSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeDialogPrintSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeFramePageSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ApplicationExcludeFramePrintSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeFrameFileTest.java 8047179 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogFileTest.java 8047179 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogPageSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeDialogPrintSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeFramePageSetupTest.java 8196431 macosx-all +java/awt/Modal/ModalExclusionTests/ToolkitExcludeFramePrintSetupTest.java 8196431 macosx-all java/awt/Modal/ModalFocusTransferTests/FocusTransferWDFAppModal2Test.java 8058813 windows-all java/awt/Modal/ModalFocusTransferTests/FocusTransferWDFModeless2Test.java 8196191 windows-all @@ -289,39 +286,11 @@ java/awt/Mouse/EnterExitEvents/FullscreenEnterEventTest.java 8051455 macosx-all java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Standard.java 7124407,8302787 macosx-all,windows-all java/awt/Mouse/RemovedComponentMouseListener/RemovedComponentMouseListener.java 8157170 macosx-all java/awt/Modal/ToFront/DialogToFrontNonModalTest.java 8221899 linux-all -java/awt/Modal/ToBack/ToBackAppModal1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackAppModal2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackAppModal3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackAppModal4Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackAppModal6Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModal1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModal2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModal3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModal4Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackTKModal1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackTKModal2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackTKModal3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackTKModal4Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackDocModal1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackDocModal2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackDocModal3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackDocModal4Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackDocModal5Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModeless1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModeless2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModeless3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackModeless4Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackNonModal1Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackNonModal2Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackNonModal3Test.java 8196441 linux-all -java/awt/Modal/ToBack/ToBackNonModal4Test.java 8196441 linux-all -javax/print/PrintSEUmlauts/PrintSEUmlauts.java 8135174 generic-all java/awt/font/Rotate/RotatedTextTest.java 8219641 linux-all java/awt/image/VolatileImage/CustomCompositeTest.java 8199002 windows-all,linux-all java/awt/image/VolatileImage/GradientPaints.java 8199003 linux-all java/awt/JAWT/JAWT.sh 8197798 windows-all,linux-all java/awt/datatransfer/ConstructFlavoredObjectTest/ConstructFlavoredObjectTest.java 8202860 linux-all -java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java 8202882 linux-all java/awt/Focus/NonFocusableBlockedOwnerTest/NonFocusableBlockedOwnerTest.java 7124275 macosx-all java/awt/Focus/TranserFocusToWindow/TranserFocusToWindow.java 6848810 macosx-all,linux-all java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java 8194751 linux-all @@ -660,8 +629,6 @@ java/awt/TextComponent/SelectionAndCaretColor.java 7017622 linux-all java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter.java 8254841 macosx-all java/awt/FullScreen/TranslucentWindow/TranslucentWindow.java 8258103 linux-all java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java 8016266 linux-x64 -java/awt/Frame/SizeMinimizedTest.java 8305915 linux-x64 -java/awt/PopupMenu/PopupHangTest.java 8340022 windows-all java/awt/Focus/MinimizeNonfocusableWindowTest.java 8024487 windows-all java/awt/List/HandlingKeyEventIfMousePressedTest.java 6848358 macosx-all,windows-all java/awt/List/ListScrollbarCursorTest.java 8066410 generic-all diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index a4513d35de05..036f80baaade 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -114,7 +114,8 @@ tier3_part1 = \ :jdk_svc \ -:jdk_svc_sanity \ -:svc_tools \ - :jdk_since_checks + :jdk_since_checks \ + :jdk_json # The jpackage tests on Windows require permissions that aren't always present tier3_jpackage = \ @@ -430,6 +431,9 @@ jdk_vector_sanity = \ jdk/incubator/vector/VectorHash.java \ jdk/incubator/vector/VectorRuns.java +jdk_json = \ + jdk/incubator/json + ############################# # diff --git a/test/jdk/java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java b/test/jdk/java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java index b9428ee1207b..6e2da2b6eaca 100644 --- a/test/jdk/java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java +++ b/test/jdk/java/awt/Modal/ModalDialogOrderingTest/ModalDialogOrderingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, 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 @@ -28,6 +28,10 @@ import java.awt.Robot; import java.awt.Toolkit; import java.awt.event.InputEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowAdapter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * @test @@ -43,6 +47,7 @@ public class ModalDialogOrderingTest { private static final Color DIALOG_COLOR = Color.GREEN; private static final Color FRAME_COLOR = Color.BLUE; + private static final CountDownLatch dialogOpened = new CountDownLatch(1); public static void main(String[] args) { @@ -51,12 +56,19 @@ public static void main(String[] args) { frame.setBackground(FRAME_COLOR); frame.setVisible(true); + final Dialog modalDialog = new Dialog(null, true); modalDialog.setTitle("Modal Dialog"); modalDialog.setSize(400, 200); modalDialog.setBackground(DIALOG_COLOR); modalDialog.setModal(true); + modalDialog.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(WindowEvent e) { + dialogOpened.countDown(); + } + }); new Thread(new Runnable() { @Override @@ -68,15 +80,23 @@ public void run() { modalDialog.setVisible(true); } + private static boolean isDialogColor(Color color) { + int tolerance = 5; + return Math.abs(DIALOG_COLOR.getRed() - color.getRed()) <= tolerance + && Math.abs(DIALOG_COLOR.getGreen() - color.getGreen()) <= tolerance + && Math.abs(DIALOG_COLOR.getBlue() - color.getBlue()) <= tolerance; + } + private static void runTest(Dialog dialog, Frame frame) { try { ExtendedRobot robot = new ExtendedRobot(); robot.setAutoDelay(50); robot.mouseMove(300, 300); - while (!dialog.isVisible()) { - robot.waitForIdle(1000); + if (!dialogOpened.await(10, TimeUnit.SECONDS)) { + throw new RuntimeException("Modal dialog was not opened"); } + robot.waitForIdle(1000); Rectangle dialogBounds = dialog.getBounds(); Rectangle frameBounds = frame.getBounds(); @@ -88,8 +108,8 @@ private static void runTest(Dialog dialog, Frame frame) { int clickY = y1 + (y2 - y1) / 2; robot.mouseMove(clickX, clickY); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.waitForIdle(1000); int colorX = dialogBounds.x + dialogBounds.width / 2; @@ -97,8 +117,12 @@ private static void runTest(Dialog dialog, Frame frame) { Color color = robot.getPixelColor(colorX, colorY); - - if (!DIALOG_COLOR.equals(color)) { + if (!isDialogColor(color)) { + System.out.println("color " + color + "frame.isActive" + + frame.isActive() + + " frame.isFocused() " + frame.isFocused() + + " dialog.isActive "+ dialog.isActive() + + " dialog.isFocused" + dialog.isFocused()); throw new RuntimeException("The frame is on top" + " of the modal dialog!"); }else{ diff --git a/test/jdk/java/awt/PopupMenu/PopupHangTest.java b/test/jdk/java/awt/PopupMenu/PopupHangTest.java index bfdf6bf2abe3..d5b10f9ebe96 100644 --- a/test/jdk/java/awt/PopupMenu/PopupHangTest.java +++ b/test/jdk/java/awt/PopupMenu/PopupHangTest.java @@ -24,6 +24,7 @@ * @test * @bug 4145193 * @summary Mouse event activates multiple pull-down menus when testing Oracle app + * @requires (os.family != "windows") * @library /java/awt/regtesthelpers * @build PassFailJFrame * @run main/manual PopupHangTest diff --git a/test/jdk/java/awt/image/BufferedImage/ICMColorDataTest/ICMColorDataTest.java b/test/jdk/java/awt/image/BufferedImage/ICMColorDataTest/ICMColorDataTest.java index 575bfbaea422..22044d7b4093 100644 --- a/test/jdk/java/awt/image/BufferedImage/ICMColorDataTest/ICMColorDataTest.java +++ b/test/jdk/java/awt/image/BufferedImage/ICMColorDataTest/ICMColorDataTest.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 @@ -36,8 +36,8 @@ * @bug 8201433 * @summary This test may throw OOME or NPE from native code, * it should not crash - * @requires os.maxMemory >= 2G - * @run main/othervm/timeout=300000 -Xms1000m -Xmx1000m ICMColorDataTest + * @requires os.maxMemory >= 1G + * @run main/othervm/timeout=300 -Xms200m -Xmx200m ICMColorDataTest */ public class ICMColorDataTest { private static final int WIDTH = 90; @@ -52,7 +52,7 @@ public class ICMColorDataTest { public static void main(String[] args) { try { - for (long i = 0; i < 300_000; i++) { + for (long i = 0; i < 30_000; i++) { makeImage(); } } catch (OutOfMemoryError | NullPointerException e) { diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index ab65a661ad98..a75eb9e3e1b7 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -333,6 +333,7 @@ public void testMappedSegmentAsByteBuffer() throws Throwable { } catch (IOException e) { assumeFalse(e.getMessage().equals("Function not implemented"), e.getMessage()); + throw e; } finally { if (arena.scope() != Arena.global().scope()) { arena.close(); @@ -365,6 +366,7 @@ public void testLargeMappedSegment() throws Throwable { } catch(IOException e) { assumeFalse(e.getMessage().equals("Function not implemented"), e.getMessage()); + throw e; } } diff --git a/test/jdk/java/foreign/detachafterexit/TestDetachAfterExit.java b/test/jdk/java/foreign/detachafterexit/TestDetachAfterExit.java new file mode 100644 index 000000000000..98c7e30717d1 --- /dev/null +++ b/test/jdk/java/foreign/detachafterexit/TestDetachAfterExit.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @library ../ /test/lib + * @modules java.base/jdk.internal.ref java.base/jdk.internal.foreign + * @run junit/othervm/native --enable-native-access=ALL-UNNAMED TestDetachAfterExit + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestDetachAfterExit { + + @Test + public void testDetachAtExit() throws IOException, InterruptedException { + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "--enable-native-access=ALL-UNNAMED", + "-Djava.library.path=" + System.getProperty("java.library.path"), + Runner.class.getName()); + // note that it's important to use ProcessTools.startProcess here since this makes sure output streams of the + // fork don't fill up, which could make the process stall while writing to stdout/stderr + Process process = ProcessTools.startProcess(Runner.class.getName(), pb); + assertTrue(process.waitFor(1L, TimeUnit.MINUTES)); + OutputAnalyzer output = new OutputAnalyzer(process); + output.outputTo(System.out); + output.errorTo(System.err); + + output.shouldHaveExitValue(0) + .stdoutShouldContain("[await_join] done joining"); + } + + public static class Runner extends NativeTestHelper { + static { + System.loadLibrary("DetachAfterExit"); + } + + public static void main(String[] args) throws Throwable { + MethodHandle mhCreate = downcallHandle("create_thread_and_register_atexit", + FunctionDescriptor.ofVoid(C_POINTER)); + + try (Arena arena = Arena.ofShared()) { + MemorySegment cb = upcallStub(Runner.class, "cb", FunctionDescriptor.ofVoid(), arena); + // will block until callback has finished executing + mhCreate.invokeExact(cb); + } + + System.out.println("[main] VM shutting down"); + System.exit(0); + } + + public static void cb() { + System.out.println("[cb] Inside cb"); + } + } +} diff --git a/test/jdk/java/foreign/detachafterexit/libDetachAfterExit.cpp b/test/jdk/java/foreign/detachafterexit/libDetachAfterExit.cpp new file mode 100644 index 000000000000..8fcb32b5b734 --- /dev/null +++ b/test/jdk/java/foreign/detachafterexit/libDetachAfterExit.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "export.h" +#include "testlib_threads.hpp" + +#include +#include + +static TestThread THREAD; +static std::atomic FLAG_WAITING; +static std::atomic FLAG_JOINING; + +static void proc(void* ctxt) { + void (*callback)(void) = (void (*)(void)) ctxt; + callback(); + puts("[proc] waiting for flag..."); + FLAG_WAITING.store(true); + // keep this thread alive until after we've shut down the VM + while (!FLAG_JOINING.load()) {} + puts("[proc] done waiting for flag"); +} + +static void await_join() { + puts("[await_join] joining..."); + FLAG_JOINING.store(true); + THREAD.join(); + puts("[await_join] done joining"); +} + +extern "C" +EXPORT void create_thread_and_register_atexit(void (*callback)(void)) { + puts("[create_thread_and_register_atexit] creating native thread"); + THREAD = TestThread::start(proc, (void*) callback); + atexit(&await_join); + // wait for the thread to finish executing the callback + // so that we don't terminate the VM while it's still + // in progress + while (!FLAG_WAITING.load()) {} +} diff --git a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java index f6c7446d1f33..d71bcf10d823 100644 --- a/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java +++ b/test/jdk/java/lang/management/MemoryMXBean/MemoryManagement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4530538 6980984 + * @bug 4530538 6980984 8311029 * @summary Basic unit test of memory management testing: * 1) setUsageThreshold() and getUsageThreshold() * 2) test low memory detection on the old generation. @@ -35,7 +35,7 @@ * @modules jdk.management * * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions - * -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement + * -XX:G1HeapRegionSize=1m -XX:-UseLargePages MemoryManagement */ /* diff --git a/test/jdk/java/net/InetAddress/OfLiteralTest.java b/test/jdk/java/net/InetAddress/OfLiteralTest.java index f9f6cdb500f0..7e708c3bfa77 100644 --- a/test/jdk/java/net/InetAddress/OfLiteralTest.java +++ b/test/jdk/java/net/InetAddress/OfLiteralTest.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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 8272215 8315767 8332686 + * @bug 8272215 8315767 8332686 8391603 * @summary Test for ofLiteral, ofPosixLiteral APIs in InetAddress classes * @run junit/othervm -Djdk.net.hosts.file=nonExistingHostsFile.txt * OfLiteralTest @@ -331,6 +331,18 @@ private static Stream invalidLiteralArguments() { Arguments.of(InetAddressClass.INET_ADDRESS, "1.2.3.0256"), Arguments.of(InetAddressClass.INET4_ADDRESS, "1.2.3.0256"), + // IPv4 with oversized octet + Arguments.of(InetAddressClass.INET_ADDRESS, "1.2.256.4"), + Arguments.of(InetAddressClass.INET_ADDRESS, "1.2.3.4" + Long.MAX_VALUE), + Arguments.of(InetAddressClass.INET4_ADDRESS, "1.2.256.4"), + Arguments.of(InetAddressClass.INET4_ADDRESS, "1.2.3.4" + Long.MAX_VALUE), + + // IPv4-mapped IPv6 with oversized IPv4 octet + Arguments.of(InetAddressClass.INET_ADDRESS, "::FFFF:1.2.256.4"), + Arguments.of(InetAddressClass.INET_ADDRESS, "::FFFF:1.2.3.4" + Long.MAX_VALUE), + Arguments.of(InetAddressClass.INET6_ADDRESS, "::FFFF:1.2.256.4"), + Arguments.of(InetAddressClass.INET6_ADDRESS, "::FFFF:1.2.3.4" + Long.MAX_VALUE), + // Invalid IPv4-mapped IPv6 address forms // ::FFFF:d.d.d Arguments.of(InetAddressClass.INET_ADDRESS, "::FFFF:1.2.3"), diff --git a/test/jdk/java/net/URI/Test.java b/test/jdk/java/net/URI/Test.java index 2cef2f964ed0..1b36eb852e04 100644 --- a/test/jdk/java/net/URI/Test.java +++ b/test/jdk/java/net/URI/Test.java @@ -25,6 +25,7 @@ * @summary Unit test for java.net.URI * @bug 4464135 4505046 4503239 4438319 4991359 4866303 7023363 7041800 * 7171415 6339649 6933879 8037396 8272072 8051627 8297687 8353013 + * 8391603 * @author Mark Reinhold * @run main/othervm Test */ @@ -1042,9 +1043,19 @@ static void ip() { test("http://[1:2:3:4:5:6:7:8:9]").x().z(); test("http://[1:2:3:4:5:6:7:8%]").x().z(); test("http://[1:2:3:4:5:6:7:8%!/]").x().z(); + // Oversized IPv4 octet in IPv4-compatible IPv6 address test("http://[::1.2.3.300]").x().z(); + // Oversized IPv4 octet in IPv4-compatible IPv6 address, stressing NFE + test("http://[::1.2.3.4" + Long.MAX_VALUE + "]").x().z(); + // Oversized IPv4 octet in IPv4-mapped IPv6 address + test("http://[::FFFF:1.2.3.300]").x().z(); + // Oversized IPv4 octet in IPv4-mapped IPv6 address, stressing NFE + test("http://[::FFFF:1.2.3.4" + Long.MAX_VALUE + "]").x().z(); test("http://1.2.3").psa().x().z(); + // Oversized IPv4 octet test("http://1.2.3.300").psa().x().z(); + // Oversized IPv4 octet, stressing NFE + test("http://1.2.3.4" + Long.MAX_VALUE).psa().x().z(); test("http://1.2.3.4.5").psa().x().z(); test("http://[1.2.3.4:5]").x().z(); test("http://1:2:3:4:5:6:7:8").psa().x().z(); diff --git a/test/jdk/java/net/httpclient/IdleConnectionTimeoutReuseTest.java b/test/jdk/java/net/httpclient/IdleConnectionTimeoutReuseTest.java index e38eadfee101..8a3d09e77fbc 100644 --- a/test/jdk/java/net/httpclient/IdleConnectionTimeoutReuseTest.java +++ b/test/jdk/java/net/httpclient/IdleConnectionTimeoutReuseTest.java @@ -68,15 +68,16 @@ * using platform threads, but it would be more difficult to reproduce * reliably. * - * @comment Why do we skip the test on Windows? On Windows, the selector - * implementation (i.e., `WEPollSelectorImpl`) blocks a virtual thread - * without releasing its carrier. With both - * `jdk.virtualThreadScheduler.{parallelism,maxPoolSize}` set to 1, no - * carrier remains to compensate for the blocked selector, and the - * initial client request cannot make progress. We could increase the - * VT scheduler capacity, but this contradicts with the reason we fix - * it to 1 in the first place: to starve the HTTP Client selector - * threads. + * @comment Why do we only test on Linux and macOS? These platforms are known to + * have selector implementations that do *NOT* pin the carrier thread in + * select() when selectors are called using VTs (via `useVirtualThreads=always`). + * This test sets `jdk.virtualThreadScheduler.{parallelism,maxPoolSize}` to 1, + * which prevents the selector implementation from compensating the pinning by + * temporarily increasing the carrier threads pool size. This causes the test to block + * forever on those implementations. + * We could increase the VT scheduler capacity, but this contradicts + * with the reason we fix it to 1 in the first place: to starve the + * HTTP Client selector threads. * * @comment Why do we configure both `{quic,tcp}.selector.useVirtualThreads`? * As of date, connection eviction is triggered by the selector of @@ -87,7 +88,7 @@ * carrier thread pool (i.e., FJP) requires `parallelism <= maxPoolSize` * and having 1 thread in the pool is easier to make it starve. * - * @requires os.family != "windows" & test.thread.factory != "Virtual" + * @requires (os.family == "linux" | os.family == "mac") & test.thread.factory != "Virtual" * * @run junit/othervm * -Djdk.httpclient.keepalive.timeout=1 diff --git a/test/jdk/java/net/ipv6tests/BadIPv6Addresses.java b/test/jdk/java/net/ipv6tests/BadIPv6Addresses.java index 400c20f2cb7f..c968dec3b73e 100644 --- a/test/jdk/java/net/ipv6tests/BadIPv6Addresses.java +++ b/test/jdk/java/net/ipv6tests/BadIPv6Addresses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4742177 8019834 + * @bug 4742177 8019834 8391603 * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code */ import java.net.*; @@ -56,6 +56,8 @@ public static void main(String[] args) throws Exception { ":::ffff:192.168.0.1", // with compressed ipv4, too many adjacent : "::fffff:192.168.0.1", // with compressed ipv4, too many ipv6 digits "::ffff:1923.168.0.1", // with compressed ipv4, too many ipv4 digits + "::ffff:192.256.0.1", // with compressed ipv4, oversized ipv4 octet + "::ffff:192.168.0.1" + Long.MAX_VALUE, // with compressed ipv4, oversized ipv4 octet stressing `NumberFormatException` ":ffff:192.168.0.1", // with compressed ipv4, not enough : "::ffff:192.168.0.1.2", // with compressed ipv4, too many . "::ffff:192.168.0", // with compressed ipv4, not enough . diff --git a/test/jdk/javax/accessibility/VoiceOverToolTipTest.java b/test/jdk/javax/accessibility/VoiceOverToolTipTest.java new file mode 100644 index 000000000000..77a1270616cf --- /dev/null +++ b/test/jdk/javax/accessibility/VoiceOverToolTipTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; + +/* + * @test + * @key headful + * @bug 8378404 + * @summary manual test for VoiceOver regarding tooltips + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual VoiceOverToolTipTest + */ + +public class VoiceOverToolTipTest { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + INSTRUCTIONS: + 1. Open VoiceOver + 2. Move the mouse over the "Intro" button + 3. Leave the mouse over the button until the tooltip appears + + Expected behavior: VoiceOver should announce the button name, + and it should announce the AX description ("This is just a + tribute"). + + This test fails if VoiceOver announces anything about a + new window/dialog, or if it otherwise describes the tooltip + toggling between visible/hidden, or if it announces + "This is not the greatest tooltip in the world". + """; + + PassFailJFrame.builder() + .title("VoiceOverToolTipTest Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(VoiceOverToolTipTest::createUI) + .build() + .awaitAndCheck(); + } + + public static JFrame createUI() { + JFrame f = new JFrame(); + JButton button = new JButton("Intro"); + button.setToolTipText("This is not the greatest tooltip in the world"); + button.getAccessibleContext().setAccessibleDescription( + "This is just a tribute."); + f.getContentPane().add(button); + f.pack(); + return f; + } +} diff --git a/test/jdk/javax/net/ssl/templates/SSLContextTemplate.java b/test/jdk/javax/net/ssl/templates/SSLContextTemplate.java index d06370ff9193..6942254e1784 100644 --- a/test/jdk/javax/net/ssl/templates/SSLContextTemplate.java +++ b/test/jdk/javax/net/ssl/templates/SSLContextTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -979,6 +979,509 @@ enum Cert { "5qh/p6izXJZh3IHtvwVBjHoEVplg1b2+I5e3jDCfqnwcQw82dW5SxOJMg1h/BD0I\n" + "qAL3go42DYeYhu/WnECMeis="), + /* + * CA_RSA_SHA384_FOR_MLDSA signs EE_MLDSA_44, EE_MLDSA_65, and EE_MLDSA_87. + * These EE public keys are ML-DSA-44/65/87, and the certificate signature + * algorithm is SHA384withRSA, which maps to the TLS certificate signature + * scheme rsa_pkcs1_sha384. + */ + CA_RSA_SHA384_FOR_MLDSA( + "RSA", + // SHA384withRSA, 2048 bits + // Validity + // Not Before: Aug 10 23:09:41 2026 PDT + // Not After : Aug 07 23:09:41 2036 PDT + // Subject Key Identifier: + // EF:50:9E:24:DF:4D:E2:80:FF:A1:D5:40:EA:B9:3D:18:13:B5:36:40 + "-----BEGIN CERTIFICATE-----\n" + + "MIIC3DCCAcSgAwIBAgIJANdNhiGffUIJMA0GCSqGSIb3DQEBDAUAMA0xCzAJBgNV\n" + + "BAMTAkNBMB4XDTI2MDgxMTA2MDk0MVoXDTM2MDgwODA2MDk0MVowDTELMAkGA1UE\n" + + "AxMCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFsBUq+9ua3pm3\n" + + "l+91IcYuJ9mc1s43bfHKga1gugFxEkUIZ5i5/DNalixeVTOBiom+m+5gKtTqJ5m7\n" + + "D/uBY8iD90UuGjYoAJRdW0N6dWuOcs5j9gHeVc+oK4sfZS/5GSFrMDrFswcORsLS\n" + + "HvT2zdKuwVO3MaQHf4J4iEBkWlieiUgQT62dwbqkVmHPbG/pooAQL9Z2L9FRFErF\n" + + "MtFjWX/VfSug4cPDrkVQRVWrBls84IYutAxxXCTCC1H77aud4vmZUbltLuG7/BUV\n" + + "F3MTVVRbxmjeINkeZ0zBa36gLbl+Ou3xTORt8/sVJ8hv7iMupSg/mD3kXuG4i07D\n" + + "27XuLeKtAgMBAAGjPzA9MB0GA1UdDgQWBBTvUJ4k303igP+h1UDquT0YE7U2QDAL\n" + + "BgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQwFAAOCAQEA\n" + + "MRehPcrW3DIq8MMTNBUTtJQ5hU1NivX4TrBBm/gjhtg8C5eYZ7f64cr8rfopiVt0\n" + + "z7+fVNOI3MyZNm7VqHZl0Il5fmxdaGHk0EZ93K/sPZjr/BodvVHx6XGkh28zhasN\n" + + "cwXZEKMXcXPsMATyauNK+IRAtoQvShsVlbecFpgIbI/u60FFZdUEIGzy5vWumfmR\n" + + "lVlvr+fF0QfIDhHRgPPMwXT4SLjcELG6mLg6p1pDjj2NCjTTuRj/kep7/B6g1v3N\n" + + "wo0KNXdl7X2kKrK6u4owCdoDqLOpF8lOO6/E99ssMAFIk9cNV6tkc7RdsAp818pX\n" + + "rRDxX2rb0c6937b+UdH3cQ==\n" + + "-----END CERTIFICATE-----", + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDFsBUq+9ua3pm3\n" + + "l+91IcYuJ9mc1s43bfHKga1gugFxEkUIZ5i5/DNalixeVTOBiom+m+5gKtTqJ5m7\n" + + "D/uBY8iD90UuGjYoAJRdW0N6dWuOcs5j9gHeVc+oK4sfZS/5GSFrMDrFswcORsLS\n" + + "HvT2zdKuwVO3MaQHf4J4iEBkWlieiUgQT62dwbqkVmHPbG/pooAQL9Z2L9FRFErF\n" + + "MtFjWX/VfSug4cPDrkVQRVWrBls84IYutAxxXCTCC1H77aud4vmZUbltLuG7/BUV\n" + + "F3MTVVRbxmjeINkeZ0zBa36gLbl+Ou3xTORt8/sVJ8hv7iMupSg/mD3kXuG4i07D\n" + + "27XuLeKtAgMBAAECggEAB6cVOfSEMVfMhepqOxr8kxfKd5Qm4zR7+QYgjXvv35yW\n" + + "jmkPU5A0ITzk+3mEDBu81N0lkk89CBsT8U0Gx+WcJxgVZpSDeeG/sry2fK8HDBJ/\n" + + "CPfL5GpDDqAH6QK1nha6HhMCkVSpqj+L5/lyKQmTeTIE6+puWvREc6jxfp3m2d+r\n" + + "VcNpqBszSuRikE4P/CXnWki+f7WWq9XkU4Qwqq1A6viy3uAx7FJH17R9RrzE36LA\n" + + "FzJYHhoT+6JnQ5TDAH/xjpV4z7IP5JNjwqpz9m1i90tDqdQJiwf/ghoQ1h0Z5Xfe\n" + + "AUytUUSjKb0IV2QJcQpXqelBWtGw1GUiK2SYE9CcuQKBgQD3qkBkTsCuXVxhVIOU\n" + + "JMRc4x6Hmc4s3+fKoLJ6a9mgm/8tKQWTmx8tsdGz9nEDvQ/PD0zA3PO3PAzkVqs2\n" + + "bXH5xigT0vDoJeyFMiO9gNh1sZHwEE8dzqYBbC09jxhQ9Dl4KtOee0KioUEW5T3G\n" + + "YKof4yYy4K1e+QBxSROmIXeY0QKBgQDNMMP0f4QKQnqv0hWQ27bWEfGc/7xG7eAf\n" + + "5Ys+0Mf6cV9TIsQd3Eid3VBHY2MhZGU6PKvTjRBE1ps5HhgIzpZ+T8ygjBCb5Zj8\n" + + "r2UOsjW+Gj9WJX0WnW2y5fHbe6+tvEfVJ4jLmmy+Tq0PECMn9MxAqf6FXr16cLoG\n" + + "RJTg6A3YuwKBgQC9mRzD9X43aX9rBsO8u24U5jvJmDAhKo5E7c89rTL7nVOWl9mI\n" + + "dUT0n61eAzO9fNvzEAkGr7uZh0x9U1pATlAT0rbrSOv38ov7Bh2x7lmGwA0j57r7\n" + + "Yh0O7n9x1M+x7a9uILMK9WCf4roTYH5JE5zjvUoXr9AtNTRfVigACDEnkQKBgCt8\n" + + "rgA8SOsY8Oy5qxXy9ydxNmrnDYxwDLmnnATVVCiOSPQv5OnD3v2qInfs+rBj8mqe\n" + + "V+8H3N5F4NRs6BocMl4J3RLyO5w2g8SDdG8rJ5bPxm02R4M0bPSoRx9M8U5FJpou\n" + + "a8fshjN5I4mL6DAxHdj66Eh9PrsiEU0Vj1rH4la7AoGAB7cXPZb7W8x5mpaJQ8fZ\n" + + "9uF6HjNfDcT9UqL46xL9J6nH9iv2dOGso1V4G7ltgK2ctF34L8Gx7OQ3VtR6lIKe\n" + + "u5uPAAK8DUYB8u1O8c+JfQGTr1M4Jz7S6jM3j6NYdE5+7N76uL7PHb6KPK8v7WGT\n" + + "S6caWOtbZ4J6h4ib5X5grQ8="), + + EE_MLDSA_44( + "ML-DSA", + // ML-DSA-44 + // Validity + // Not Before: Aug 10 23:27:48 2026 PDT + // Not After : Aug 07 23:27:48 2036 PDT + // Subject Key Identifier: + // C0:32:22:4B:DD:69:80:A4:FC:7C:18:EA:BC:CF:9A:58:37:17:A3:45 + "-----BEGIN CERTIFICATE-----\n" + + "MIIG9jCCBd6gAwIBAgIJALH6wt0JDw/YMA0GCSqGSIb3DQEBDAUAMA0xCzAJBgNV\n" + + "BAMTAkNBMB4XDTI2MDgxMTA2Mjc0OFoXDTM2MDgwODA2Mjc0OFowFDESMBAGA1UE\n" + + "AxMJbG9jYWxob3N0MIIFMjALBglghkgBZQMEAxEDggUhAN3vfRhdse0P2lPsZf1p\n" + + "SKtM9QZTid7HRjpvUta5RTIgRGfMmXhxhfUZ42HJYDjpJsTlgEOdPbI2kgVex4gR\n" + + "PqrSP71UZCnjjNjtFtdAlROpPjJdghx9Ts5qLxlRdWdS8MGxOLpZXIDK3lKPZAhq\n" + + "tcnoEeSzm343vHb5L8n9MIQ11dgcR3zwz91EW7YtO2vyjw9DSQ/TwiSXVVD1VdD5\n" + + "U340Z/SLR8HhrgQfxn90NAX7t6NCtyKgQABGAaoAMu1NDOx2ERbgUXvPOKdm6vQe\n" + + "5Sr1y/VuuncrvajTIrzaluN0onnonvOqUHdpoYKJLcDfkEQrR0QMWvZuck3Tkuly\n" + + "uN1mafmTKGctKdKZRaL/KGSBmQQFF5BXNQqZ0T1prYqqGqEA4+OoFsYQ7CG3/gkE\n" + + "zhDrmpkVfoDQrLe4SkfujibbiAPD7zGFsoufA0iCchTgTcFtMHIXjURh0TIsECKc\n" + + "Z07Xj6VZImQvU2jVZlu1SSxpNtJDE9XE2zutTJNEjqwSYjO9b9RhvSZEv9buhON/\n" + + "EuMQ0VtgzLryLGjhUuGezoKbWzMgWov5gMwc9Rwjv/mB7/dnWP8M7ktcBRrpbVEc\n" + + "STfU7GKn+VAtzYJXED3tv892j3GBhnNCxYLStCvNpUODt3NEsv85FskeZTmOrZVW\n" + + "7DKUN340c4oEsnK92cHnkdmupoxCr4mbyHjslye7cNSJ+ir6yG8VbUVlnaPNtbje\n" + + "Ilbta0hSYcHtqUSG/Pr9M5sSQLLej8CB7EiMVQfVWjjUdPKDgJ8euwzZMkkCuMRl\n" + + "YNwl8NK5G4wecvU2h1vlpRmBGUQh2HrEBXpb0tZCZ8CinihmYn8j2FPblMUcLVYn\n" + + "2xzwf4wrT14WKlI2rLHGZE6eGL9tXBiOISZu/YhYo4dj88xlxzqO9okTeI45T4b8\n" + + "/rTHsKlidx6J/2txsd70XiB2W0WrFXY61VhMPHI4qFwtPPF6Egw3yyYREuMkZmS8\n" + + "wBu8K4zLhpnRy6m+yreQewzjd+vi2RYjc4utbsH6YQJfFyUntOpgYM7jfHWuShkX\n" + + "PD4tzqLnLDZrMex1HfV4DtOi/Kjq1D+Q9XZCzMZZxDITVPzwosks7mvZ9gZnWULo\n" + + "9f6QzxURfdrUOyKjxEu2yLk9lsvv0zUPHH3ttEz86wzVdf5lF+NOAv7rQFzdC0Uo\n" + + "gMD0wGef2sDvlsfFC+5JvkhAPIkKJC6jYnKSzLEW7pSRyHjrKWALwjhIbJFd5Ga9\n" + + "LNc388pmaJ8Lm0riU3oMBhXln/X8e+mU7bnPGX40UB0owVcjd5x2mk+QNdATLbgH\n" + + "HbU9HqviB9TvyTAvHozgzF3M/xSfySKvJInzeZqNpj1mD43hUHND1e9dMxVA0qXi\n" + + "e0p5kTEMOvZmAvGUEi8a24QNpet2GQg+3Kdc3f9856i4e06wO7hQnTRMzNSc1aXP\n" + + "jA376CgLZvQV0lUs6/iOQ/QgNXLNzsVM+UxOcQpOchwwfIttZWL2PAWRTCaGFdvD\n" + + "GgdkLBLButwO3h0rxfoWqL7TMhl0gtiGs2oAHFXi+tN+YgMm1/4hUUzFBR170Nav\n" + + "aO4fSb9Y95RYUw8LPbezGaqLLu9BJ+YT5ukRtwLmD67qynSWmy5m5NjFm38BIj9B\n" + + "n9zOnknSAu5qPcVT8C9n/3YJRn+dllzIRhzjE+i0t9PSV32xEGPp2cTRwXaMNfy5\n" + + "/Ptu0e59P2il+WvUoliI6Zq2gk4RBz3UVlrf6WGG/n8vCES9pE+v+G6Jxrd4Odmi\n" + + "DHOjQjBAMB0GA1UdDgQWBBTAMiJL3WmApPx8GOq8z5pYNxejRTAfBgNVHSMEGDAW\n" + + "gBTvUJ4k303igP+h1UDquT0YE7U2QDANBgkqhkiG9w0BAQwFAAOCAQEAndMnHyb/\n" + + "ITaTjPvWrN/OCOOWkQ3A3S9PtVnSt7ewdVrjAOIjQ1CWRP9IwQoO013H+vFfkuhH\n" + + "dmHlJ+ZMJVjUm44InfyNI4v9M/B7/wjkRNN229LvBGZdRqXfofc6mVm78go8Ebtp\n" + + "j0uqLNLT196qhrF6EAdFf10qexlag/L3plo8W762tPLI/NoAk52nTKScL6QbGtoA\n" + + "xjq6eR8+f07MkwQioRKtxj10QnvfDu4nXAq5a6kQmUcl1mG9LRT2HxWQO3HeNa2K\n" + + "vzXoVq7L7asc5WuGvWkDYFbm0ljXAnSQydfVhXNHDuhzzDq91iF5EIbfzVWdQH6p\n" + + "5FLU9zdmfy09Iw==\n" + + "-----END CERTIFICATE-----", + "MDQCAQAwCwYJYIZIAWUDBAMRBCKAIHp7jGjtPyXkcQss0wvAxAPRcsOCEu70gFLn" + + "iB1c5KT3"), + + EE_MLDSA_65( + "ML-DSA", + // ML-DSA-65 + // Validity + // Not Before: Aug 10 23:27:47 2026 PDT + // Not After : Aug 07 23:27:47 2036 PDT + // Subject Key Identifier: + // 13:F1:89:65:1F:E7:C7:1A:51:57:F0:09:7C:B5:5F:25:69:5D:06:55 + "-----BEGIN CERTIFICATE-----\n" + + "MIIJdTCCCF2gAwIBAgIILpNJyz+5le0wDQYJKoZIhvcNAQEMBQAwDTELMAkGA1UE\n" + + "AxMCQ0EwHhcNMjYwODExMDYyNzQ3WhcNMzYwODA4MDYyNzQ3WjAUMRIwEAYDVQQD\n" + + "Ewlsb2NhbGhvc3QwggeyMAsGCWCGSAFlAwQDEgOCB6EAx1NliX2iHKzfas+AJqIa\n" + + "ENXiCGm0mesKBsdCktd7X6HXoAniNGGytBsqFXcPJk52zjBTA4bDTNldugrTJyGS\n" + + "QlL0wUvmSL1ZbpENW4Sr2J22D1AWmPtcHOME9PBPueqHCwt+k1xTqnwIKUttkka6\n" + + "OCRWRTP3yEFNA65sLf5rOEArj0PIP4YFxfBQ80zJqtMepBGnD1PDU1qdPi3qghFR\n" + + "g6Ue3mtj69Z/LnkDXD88Xj3FoqEJSE427JdCN62Z5P3sSFwCHjEhRwOxSQdZmZmF\n" + + "DlOO50O56gV6zu70S0WszDxGpgzA8pwF3FesoDLmaeOT7vEwCYZQ38xgWk8QzMIM\n" + + "jVxkSjLEvO8zw87NjixXIorsDjRbT8ArLuVGxmFiggqLYopjJBGH46KUAWV0fNDB\n" + + "WLZ/3/Gnf2bwoU84rEOZDrc3g2aGLEWRp+lIydLOLyQvtIynw3wb5EW2sa7EEcW3\n" + + "qEcdS+naV3PXEyq5UW5A9eypTDIr1CRqCkIo/dShxszkMamHS2UZ9kEzWDTRN35o\n" + + "QLWBanf2QZCiMyMcTp77eioY5hIHsVM9JY9jQFva3wTTtI9b1Vhv5PP5F0wC/d5F\n" + + "rfo3fhc2gidO497Wkk5814dRdx4kJZmaZxGYYTHcTuQxbEP10pJ+Z/pakhE+eRk8\n" + + "xicqMVJstNw2nuTxfIDinp/N82dUcVKxB5Sm6dcy7YqtnUT/ltFFrpIB9PmIUffR\n" + + "NcPUeySzdH03wyBmhrL5RdjGnA7o+MEiO2wRGgla5g6SsKg+NMmNy67Gat6XP0Qp\n" + + "EOtHah4kaREO3V3Qs5MgtBBFjOxA2BQy6ybNheBDdWHoVo17cSLo3s46RJVGSzIM\n" + + "MFxZi4RlHmKqqHitREInTe+CypNvz2uLvUfUZ+pg3niT9E302k6IOzwYbWY3ybcH\n" + + "M8bY+AhmNsomOdW+HBamSemXNPZdE71cvq6zwEq09KQTIGOXY3S/IAl3rDf9XCR6\n" + + "9+J9LJf4hms09KurFjsetRAORWnkRgJp7y3R7AnKhqvez/hZzac/bMhY2EBEZsGY\n" + + "700D9yX+ZQ/mK2253B6VQwu7CELaL6TY3vLH0hZz+ER1SZG2RLmCnlYT9kyqxkzm\n" + + "tmWfVo2Od39jj7kmiXkOR13ZjzgfYl+gnuKHHY8z5olmT/HzVs58lP1OLokz+Tbn\n" + + "N1Ga0xWO0SrHjZEGSO4aJ2Dddm+hbgxLFPUO/6sJrOQGeUHJzm1h/8gbpIaflIO5\n" + + "E5ZUltXb8gVlrwqNloMVSdrKmC8byfR5R96mIWX81cp5EH/WTFkL81zQXmCLLZSB\n" + + "xosip08DNQ9NnJ+yt54S4+dx02FPvo+gSut+rG4qe6O/UFlJ6KnLssWmGwPfznyb\n" + + "V5ujKtzecrXNKjm9rKOGTCSO5VH0y6RAr/R4RmlPFtOGaRQamQJ5+D1Metk84DmL\n" + + "o3XXZ+semLk9+pTfiHORE+lH/Uj1K8i9YJaykqFlR0FP6/1wduoLMK5dD861bR+A\n" + + "UkIFywyO6OxiOmOEaWePM59TY1dqP+LpLCpfL/8Fk+y6nwBYokO60zEj2iGCxSan\n" + + "ky2ULwjptxw6qMAzAuTqm8cV/fVcqynNlAu69jobR8VtLAb2CwsYIQUSISs5UyB4\n" + + "KI7ungFa0x7Z+w2appeAzcHMbMjzDxCF8FnPRN4NGWIMfOBqklrSg5sbeTvh2LBE\n" + + "Wryh4uy7yq5LBCnl1DChu8U/QBo6iPtMp+w8WhuH4mIySM+t+V/QeDGHltCeVmup\n" + + "U4+abJs6LDcDBIC/EbE1D4DfXyLe8ukB9o6M7UNI2lJLGHMYrUlG1xxmq1WGlcCp\n" + + "gpDQST5Ouc0OKgLuDYT2+KvAWwJ48Q9lAuUPDVyKLN376mdbWG3VaXk7gi6oCsdP\n" + + "NVkQ9qWj9Fx29Nz/J4d0re/Ibz+yplRZbTsKVR0rxJ8VNx/hdptTRwJ7zs+8WvsH\n" + + "JwE7Dz1bHm6Hnv2Z+1thmnPpkwMLZhFfP0Jhn30nFtEpEq+Bqxr/V/Az04LrmChq\n" + + "mfhPY1ymUeNWHv20DNOmB3ih1lp2VvoIfN4btTQyD1ji23tuyRyb7gVwM6m1EK3F\n" + + "uKxpPJieWO25x1QGGqH0Ww5bkq9YYLoLZz0SZh8Vl8LkxRQ5FLQvDGwuuhjge4Ts\n" + + "w9YvLIX/WVtwfARTQK3K1vPy6M7oWnkW7ymEqB0CQR10Y1/eAsnM8ZjG/gZm8mU4\n" + + "B4ojJBHJ1bPK1NXiXKGMc1n63U0gg1ovrVWsOQMv31Q+hJOA+Hh/ydxhJHEN5HBJ\n" + + "QK9+r1TgbdS7T7yeSh2ga1SdvMyr7GbBO/kuDZzPBVaZYgEBE8NDB6uBt78HuM4o\n" + + "9pI3JvFRaCRKLVMXonNFzWGTS5w2Yt7uhrJnL9v05qK1VYnk+JfU7GTnqMlqDkf8\n" + + "K1njPQC7RN0vHuWQFLxOKs3VtxCKnm8awoD6Bg+IIXTTkCIzLytGQZCXjs0o8bPO\n" + + "JNbWmUlp2w4sxq+/FuZUmS8dgj8GpETJh1zLOWkQoQs1VuTHlrzQJnE7bjUz2Wzt\n" + + "h0d2oON3ztIVUMCnbdzGIbUrWdZ2Yyqpc3i3/jN88DYzH+cbfZ8Sc9l0XAxRymxt\n" + + "bJtJQFcm8enEQKntcUUgXH+jQjBAMB0GA1UdDgQWBBQT8YllH+fHGlFX8Al8tV8l\n" + + "aV0GVTAfBgNVHSMEGDAWgBTvUJ4k303igP+h1UDquT0YE7U2QDANBgkqhkiG9w0B\n" + + "AQwFAAOCAQEAknJdGMDpfNEE2Bn343IdKgFy9ntpUBl299Li3xav+DNLi4VlXWQO\n" + + "d/B8qu3dpJ23USibqc87CEvEZglNHWg/S394bruAhkWHlgzwD/J6SeNjk58OYQ79\n" + + "uT+5okx2ALoFmXa9YqXa6H9d7DKLxVdjBB5pWJbY0fuMFbFl10zZ+BgHkB34+aeQ\n" + + "BLxDqT3rI3JJb1WRNNgq9OlCYZTr8iDqFuQR5bCW1yX9MaaxcCeY1GrHuOSMYp9X\n" + + "trJn12uNqV8TdT0yPnqJOOm8sAJeCqjgil5jAienQhZnW8duxD6Xepj0oPottbk4\n" + + "IwWgOeLjoKhqhLxYkTlCpO31JWbx6B7bGg==\n" + + "-----END CERTIFICATE-----", + "MDQCAQAwCwYJYIZIAWUDBAMSBCKAIDX3Fb2fp4CvjPbCRd2f8QN5fb1ljKOdopRe" + + "ATnEmhkP"), + + EE_MLDSA_87( + "ML-DSA", + // ML-DSA-87 + // Validity + // Not Before: Aug 11 04:29:33 2026 PDT + // Not After : Aug 08 04:29:33 2036 PDT + // Subject Key Identifier: + // 03:55:E3:57:8D:24:B6:20:E1:32:6A:6D:40:D4:A3:2E:1B:9B:EE:01 + "-----BEGIN CERTIFICATE-----\n" + + "MIIMOTCCCyGgAwIBAgIIEFwx9ugPqLwwDQYJKoZIhvcNAQEMBQAwDTELMAkGA1UE\n" + + "AxMCQ0EwHhcNMjYwODExMTEyOTMzWhcNMzYwODA4MTEyOTMzWjAUMRIwEAYDVQQD\n" + + "Ewlsb2NhbGhvc3QwggoyMAsGCWCGSAFlAwQDEwOCCiEAIHgviPJE7aPezmSQFsjS\n" + + "dxVt0t4Vib2uZRudzku0ojkxCCu7I6dAWNpxcuQUbVVuV33eb6tlc2qrvYORY5mQ\n" + + "1J+d5hkxbUJm7l4Js7FyliImRfNmoTe6w3sw2S5Hkn3knfFAtRazDaxjk/N+vIkw\n" + + "Mt0mwTqF644NvOdRIfD7E1ADoIgdSHLQwUshRv/NPRPhDIXmCxwhHh2x9mf3GtnX\n" + + "Kgy2IL0a6uqWSysWWU1/Ue/xLzNBww8sedyUwQJs2mW6nYb/JknNSnKiWGYzU2vB\n" + + "B7lQMFmgAqEBtoXhriF0AtiDNqO267XdbkAFsm5A7/+8hXy4q78CSoUjrm4aREsi\n" + + "PwdMiRLaKspbGtfUQeNiZk+qvCr9WMiMcNnxY7zcOsf3ipVvTib3M0p2CmYLPI7k\n" + + "K5zobAtYOVYuvuquk/gLiW0AGsj7g4NKN52ILxYHBE4moGby3taZkzd4XLoux3QF\n" + + "qoMTU3uCpzLbvFqK3ZHU75z8JYO2C6MHiumSVKRZpLRTYpMtUXHHR5Yy24nguQHB\n" + + "KjuO5zdhvnrFurYSUXzMgy4wrpyRcuPQGROsMEia6OjWIR3xdxg9H4ufEGnzqlQD\n" + + "kllAAH4yI+KockUHBdf8Qdm5H+0yxWvdX8eUP90xXwlhXrudq/04P11PxbgjDwFX\n" + + "fCIOfgKslC9aX+s6CpC+gtUHhCR08O49sivkAc169nl9MY8ORgMmWQ7N3VIseZpG\n" + + "SiIoTP1t7aEpQ2QyyJ8LU9JRyKT7r3o9ndf5zj7sgDsT4OyCEBZQfwwctXfD4T4k\n" + + "3InvXSmC41tv9KjnEyPHqQjQyDJTNgsCve4IaUuRbczAgxZT1SZKGw7AX82dGCUy\n" + + "VEA4C7X20MzHYi8c1+l2aAE7s2SdhQlkMLGzooCWJemPYrS14zgslhtA56V+1GZa\n" + + "uXP9K9IeAc73KqBYGEMiO7vA4nbHRVk9+yw3J54vYpGP4XK91qxut3x2nnGCdj0R\n" + + "bl8BiI0OsLS4tkkfylc3PI+QS5IcRtySyoEqZZTpd/TAn91Pl3eBbV/5PGfRSK2E\n" + + "UADs9+6AM6TxkK3FyXtJHp24lZdUqxkQgzi8uJkQoEzxF2LgjmCLeir3DIqC1w6O\n" + + "o3keZ7h4Egxc2nhsu7QKRbmChowlnH1B9O8hjMMZI3irHb8vyxmTuXlbMs3n3Kkc\n" + + "UIhJutGCVlYgMB9DDevWS1hBl2RXDzA+vaqH61cA0CEeCjWKqxFaTy3F9GLPDKCG\n" + + "x45l8k76UKz/+egnAzhjsXyd0zCsMnAb0jtbbZ1PyxOXYhcAh9SnJl8x72maYm0y\n" + + "QeLiNez+irxAQAo6yYYqMIsmx1O05nnmaG2Kp9e2qoRpcyrXbI2zQaeEbjdsaEfX\n" + + "yya5g8ArdPt4wbPMHZaYfNZ5IT4YpOSxJFTav9AvCBD+jJTHHgTClND+pxPl62gU\n" + + "w5bP1kyoTRr7Krv7z94ey/kO3v1qe92xz09pnFwlBIM9yIzvqwte/p2aE3sLnwP8\n" + + "+LaGYKRk2GQAZtRzabOoFJEWi1NaTOMM8wYJNA6JrqAOudlWUfazhvo3W1QEDwtG\n" + + "8a3HPiFh73JV6Mltqo6AD00oJD08OL/QJwt8MkNV0MnvjY0YZZxybGNy62JTQ0gU\n" + + "As2to17OBlhJ7pg/udZZ3pdyHgBL+XcvPOSLzC6EEv7pT3Q7YzizVtWYoLUXIHbb\n" + + "JWcjMAfq4j8KamQCnOsiYZxRhJVneovsN3VW/9a1Friwmm/mb3TYfYxfwZgzBDni\n" + + "8Ml00a3QgL/3B3Lb2QzOqPoAVlzXsAPKs0/++Gb68sx1GyX5zXMswh1IyQ531UVC\n" + + "4j+Y7ARXjEkvVw3H+HBxJF60B6OM9sZEM8j3u/tIby8BvUPI2H3GzXg4mNjNB9in\n" + + "nAKhZWUJLG1zbNLs+HZR5gIMJqpmdV+h8hvWaCDN4MlBkzNAwMpdyhriqXIP4kne\n" + + "XXLt8Fzd0cie8LaJB5x2yo/VJcY8Excay2JWVlmxWGdr0y+IzMr+ISoYZgTW+l+C\n" + + "B41yM0alhfCo7+Hj33y1CK3JXphzl8Gjk+h5fS5jdvGbKi55tAGs+iWB3/7Zh9gz\n" + + "aiVl5TeKO0SLGAxJKF5RXxtGMAwpT/qQkqC5aVut742YdPXzPAXVhEV6P3664cT0\n" + + "jZkaoGdD/6Yvw7xv9TcUUWZTixPSHd+A9Es4VT67/lLd7GmWG60sYDOHrZBDViG5\n" + + "3b0hGYLKNnUReq4xcpvCQzvix9nDKjg2nNAixauAe2iUjgGjZrGT+Au/TSW1HJrH\n" + + "1CdpRYgKOWIgi43dXoH9nj5sGyNIyWvDTwkipR/GnDvVW7ejdMzMlwu0geNi9JwQ\n" + + "1gOnN3NEauZ+4BPGBCnj5+mlSTZrQ0D+L1qvVprLpekybkVa1lmCOsb5+ZFUa9KA\n" + + "f+0rzmMHFpbGpliPfRsI8AxVhrkHG0wxMLKh8DOURbT1we5q+VbwJfUjIXVE+CoH\n" + + "E7BMhVEGchsmGxxc85iV0NhVsv12UYox0Spwk0YrOXC2FgXP8i3YCNEqy47ZyMSG\n" + + "qzK/y05fMtrKu3gVAdvTD8nWWZ0+rX/m8hX4o9ZICmZKDzkTdZoU2ckUE7bwIgPU\n" + + "zEPrHY0aFHtlDUFEoCIf3hfaNFapWU5Qp71AJyFo/s+eWH2KIKE7ePSH2glsvrB5\n" + + "TH91r6l3wpnhBkoR5MFFOXJdFT5T0qFl79hiwM6qGeToa58IyVTaf6VFY6W7a4xq\n" + + "+UOKpfyNUNAUe27X0yVw9L6KPXGV9IklfS/DXMjTGQBQGOI4W9mHkKByup5XCkJC\n" + + "QenzOehubqH0BAs5t8pZuZlk749TJa/eg1DW3QeNYkk1prfvAmqfNza/jqtUpb5T\n" + + "8+8NUQyioi8Jb84GS7ocBr4UrlzTNZIinkRBHhlKzs9J7iMF9pqgtnI+EkZOnlhv\n" + + "F+CbLw9L6FQyx37Mz0iz2EwbHrMTXTsNZaRF44K6Po0ITNzfd+JFdXFQV0PNfw/O\n" + + "LE5hBqrLPCoOtD2nJgq05quR91bK3/Alh4DG9JaBcvcgk3C+xN8hcrh0UZzUO22w\n" + + "Nc5Ak0O2xu3JLmegzQBrOSSFefWd8Elj1JO9JJTEK4CO20lN1Ir3gKAuyYVazkZd\n" + + "7wKv9cYcvGsXq6kvLFnquj32U+h6NygEH1BDHJHCHwIe6jlxufVrS4Rcczu0VmXL\n" + + "t7EcDhhbRed6MUVk97rI4phkWA2tXWQjITuV+TInEN3okTJcokAFT1yQIu1gbZ+K\n" + + "uoSnHOD3NYWwXfizTe9rB1IUF2tTEhQ1ocYvsZLDBSloaJrb5ZOGGV8RYW73DTzb\n" + + "/+kjnBTac4SncYttB1DqJd3lwj9nibeUkfhdmaeUiP8X7xbMOaVpeOWZeIs630UD\n" + + "24I9K4ucdx9o6UQbrfTKgP1z8DscIOZ06uLJ+Pk3gIALHm9QJiLoYzQzlpbv0hdO\n" + + "LQmBOblKnzrjCqhy6zoCEFc4VHM7wDWs3ZMirOgTnyfHo4GFMIGCMB0GA1UdDgQW\n" + + "BBQDVeNXjSS2IOEyam1A1KMuG5vuATALBgNVHQ8EBAMCB4AwFAYDVR0RBA0wC4IJ\n" + + "bG9jYWxob3N0MB8GA1UdIwQYMBaAFO9QniTfTeKA/6HVQOq5PRgTtTZAMB0GA1Ud\n" + + "JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQwFAAOCAQEAhGFd\n" + + "OOemXAbB2hLBq2XHuBuJgC5hl9L1KnDSEWMfeei8m+WlannGwLv4RUQmOyfnOv3v\n" + + "ZuPHPKXB+A5l23sVob/62Uskt65xvUbQFj1hIwoyguVabFe7AuLd+jrPOnEcZTw1\n" + + "I9LHBIkGmkjRegkuvRBIUAojCJBZNkvwB5osmrPYh5i4uCpJLH1NMRZcAdyh3HbV\n" + + "VAK0PCnZkR6zasG2gqeES//THhJS3k2DfjWOhNLPAxhu36r48hCpXB9hFAQrfgtL\n" + + "zVwvWjxhRonMIAcTSWS1gHkL6SHPA/1KwukzE0J1DkqPqe70FhoFmBzIOExvna8w\n" + + "79ZgnGXydxo2RmtsOg==\n" + + "-----END CERTIFICATE-----", + "MDQCAQAwCwYJYIZIAWUDBAMTBCKAIOAYEGabtbngvedrHhAi2Qkck+7N1XRZ3hCy" + + "dM/i7lxJ"), + + /* + * CA_MLDSA_65 signs EE_MLDSA_44_BY_CA_MLDSA_65. + * The EE public key is ML-DSA-44, so TLS 1.3 CertificateVerify can use mldsa44, + * and the certificate signature algorithm is ML-DSA-65. + */ + CA_MLDSA_65( + "ML-DSA", + // ML-DSA-65 + // Validity + // Not Before: Aug 15 02:44:31 2026 PDT + // Not After : Aug 12 02:44:31 2036 PDT + // Subject Key Identifier: + // C3:9B:62:4C:5D:45:E6:6D:6E:8E:7C:E3:93:EC:8F:0E:AE:08:E5:FD + "-----BEGIN CERTIFICATE-----\n" + + "MIIVSzCCCEigAwIBAgIIUXHS+SGJ3rgwCwYJYIZIAWUDBAMSMA8xDTALBgNVBAMT\n" + + "BENBNjUwHhcNMjYwODE1MDk0NDMxWhcNMzYwODEyMDk0NDMxWjAPMQ0wCwYDVQQD\n" + + "EwRDQTY1MIIHsjALBglghkgBZQMEAxIDggehADMYfg9FOy5Tveji3b4gh6Ev/nbI\n" + + "E1/FkracDA4+roOGgzg8yXYbmaqE/q/7uiMBrgFM9c11Kr5JEfYToV9BUcFh5G9K\n" + + "P4070orfM7zfZyivRPC7pVAQx20i6xMhGkR6ehPxVj3irqZpjPhlgk2AmwWAqqxh\n" + + "+k25c+OlxvZhiMlyz4rGA2AO3J58kINcDFBLSA95dcvq7MoeYshLzxqbwO3bOg20\n" + + "KYGaDFK5xWLjAbTJk7QXJ0XPERImRXV6X9rOZcPweVwXT5edgo4pbFEmKgwvUoMG\n" + + "GLnqAQPYIGbvBAO7D8ySBz3yh50fK2Vjfc2KWi0u0ZArc+hQu4XcYnL3wxmkCj5U\n" + + "VnE7AkEw3il2NxVi1PkWiCa6HAO0eYf/NET/EoZOAk3WWEHkN4XjtiRgZYaH/BFh\n" + + "h43d7e9WAMLjevHqtaVOEGZGfYE+4gN5GEH+EoHoAyGfhrCoXRIO9GXOcQjcTfGK\n" + + "ZzDh77UQdCq1ayvzREjIfEZNSJXGPGluC38BoZClM9T96xJceGQj5naCHK/Xtk/0\n" + + "gZl+LH04d+GhqayLUxy+43nFotUdCBDxZvMZHnWuc5uP3lKwnqO+ECSGutW1Czsw\n" + + "58qTU+Tph5vki9ikBDYnXywoWD/bVIDW+lHkv8448LhY0JPtoquVjYP0/XPwbDdC\n" + + "1bJWsFA5Ipjk9XG0pRRj3BOhdjF38vhQ4i+6uGv6FsrUWY0shpRSoBKoIiJGFiPI\n" + + "epf1SBdeljPhjgweKJ90mZfO32Uu3CBJqMuzoL5Nl5MZ5oR/KqNAFSmAB3N765lQ\n" + + "AX1mc5sBai0HAsNohkwrVvxiYi5Akd5M87D082PY7t6oYimuJdX1MWQq0KT8AX8Q\n" + + "bbpbSXV3xYWGy2W2Gw+0CzatVJ6BdYpm2nQC3G8CnzbGYcKZwiibBhTaua7fy/c4\n" + + "9r2JUQSU3B2PM14ERmr/8+MI+PxWZ+Pa6sWDZawT4nXqdM+EZxkK7SQcfRE6AgbW\n" + + "cRHRJj2DrCmAhCKQ95AFSwZkGQQQQ4hXnLQj2PQ0JI4k4F59HayXIpmjdVhP1FH3\n" + + "Q64LIdJ5zXJg2k5gn7TSiaIq9Y7oj9PyNLxuG8jYaTY3gSrqwLwMaQEYoEXrkLtF\n" + + "qRuwRzjgYgOwCpaH8+MEcfwXg9iysX62BjbXBfwxSJsMDiKdNpEDQJIcUfhpIAdA\n" + + "TmZrwISV1iyPA9UwII2OV+bTEW5HwA6cOzHfoqpQBURdxIoy/5+0ZcdpwyQmkDTn\n" + + "cnBMX+VI7pl8yl5y35krr26mKEsFtOY05k9hl0NUt1ab8qn5qGwX2LBM+/rCljqC\n" + + "4QptTHWf+sn2oDb6lMC+sCkv6SQJM8bVBvJb3w9bcHCjxHn0ypTeI4MDnPtGX97U\n" + + "WJBnYFnzB62HB5WP5Ff3s+c5PsifgcuIVCadwTwRe3/vu8gdvbrSRJ1vx1ncWhOy\n" + + "oM842ajfn3Ml69zTbQjEG5N6ZclKvabxYw/76rz+rMftzWd55wW2b7ZMTa3+htYc\n" + + "3zIa1KJCysR+BMEaAnpAx4CkCsBMA1+4O7OqyMh+BJ8gJugOcta+ZB26Dle04xsO\n" + + "HmOlC63dsT3o/niPSdh1jmghTc+vW0q+fQc+kbX1BYFtvnZAqHB5z6ZXuPTad2Ur\n" + + "1iHZa3e5fXrVJEZdanmdegWplWeS3MYp7OyUQTfUkFIYrnMhk+aWglDGdqQF4WoZ\n" + + "yN3gXBBZ5eIOJMdG4fcwF1aRKkLzcclUJB6ivuBl/iU1k4gLksS5dN9C0XPBVc8O\n" + + "slHSnNWlesL+rnPRbyC+RMmBQ/h21pXUDDEQc+5E+T9efrpy8qd3ms6Nzb8bSatN\n" + + "v9ub8tde+5GI2fsxrd4/S952dEdvR1zOz/0AaWjHhAKCzD9/PR7n8uGv38di3bE2\n" + + "IT5C6nV4/BTtEOeBP12reEkvGGkky5Ucb8ptECTNZl/Dpi9A5JS+3y7W96BRSJ/O\n" + + "crAhI1FHVMHyWtWnXuhksR3tMb/up6PBD6iNAiiigy5h8JQx4N3E+XXGouz5HBMh\n" + + "JN2v/AdnTC2pUeZXuZ86VMdUSc3TDMN7+67xmiJmbhv1tPsEFy+/6RUTFSN1LLlF\n" + + "FnLudwblh71EoBSENer7+M5MyH0JzsEKI7MIE94w1Nrral4SgQs84VGX+rfvYvcI\n" + + "tRNcW0AetpGZtcEXHsWpxnq6UkHtNEKcxAEFpeFdA0ZBdn/t302qCvVGWSdrp1jE\n" + + "scj1Z3S4S7baBZM4lipOGQd1Kz1EhGmPddb2EA97CoS0BVYc5FfEWKthutsu1T2X\n" + + "/gSd1gKaQPA60z0ksJK58Uhgf4EvoMjBjCY9SX/CdJiJLh64W80R8m7cQ82e6wmf\n" + + "QH0SXhwmIsSOB/pPbPpYQQru9pLNOELyae4dRvl88JyLpwbAinxIHSEHf7rQBNbI\n" + + "6D1eNjIkOP4PWmk2MSgDdNe/wNWPTvqObwQuNYc/CJ90N9cCg/0E6JLOkeghA2L/\n" + + "5ehUi2BuCLSdRCYUQOvsMmK7j1CNn0nm5YXqFodg1IsvOuQmj6dfdS77S5OoX/rx\n" + + "kiU5rBxito6YdodbUaNcf/ifXwthyumcDAZLUP8Q4xk8kfph86kDje7ZJbNS2QoV\n" + + "Rc8QRK/k2w1tF9dZozIwMDAdBgNVHQ4EFgQUw5tiTF1F5m1ujnzjk+yPDq4I5f0w\n" + + "DwYDVR0TAQH/BAUwAwEB/zALBglghkgBZQMEAxIDggzuAI2MHWNWD9xtNM09Z9t4\n" + + "qKg1ne3sVaPZuAEn7XzyyFLaozTUr1ffcLWY65IfwrWHJBRm9NOi1d4dpED47RLz\n" + + "L2AX9xWs4tbT6bbyEHWkZtAKIF7MulAeedBTuomKEZtE8/zk20KRsWXu3PMuoknR\n" + + "cs7Gtq6r5zEsmQ32JWJCSxAZK1UEsEM7drfCQA1Kg3Sqxtclk6oUlmQ9+vttAKj4\n" + + "jU+FGhvF4XcaRa8x6PBPzmMA3VjdUIBbEpmwZmocUTmxic9Khf5pESu8uzXijtgN\n" + + "Iu8Xo3aOP4XPuK+ZhxFJIQQfAyRyBhS7+7PkOTW/IrEtA84p84H+vqHQSFtai2Cv\n" + + "LXL4DbkDAZLn+CC3KlxJfjK/VlZq/Nkf1Sjb/Tiv7NkD9drwt7OBhT7qFQV4lmit\n" + + "/jq6v+HVIvD69Z40fxNLiLWh6YN4ecmeh1v4vF0p9cLzmLbcGuQ3CUem4B3XvTdD\n" + + "6QTsVrPaRd/YubxLux0KzA8UiRZOFXT/96OYU2cViNqtbz5iKxDhme//jnOjsCjN\n" + + "XOPdts9Bg9YeTwUFiacBk53rY6Z10MbvMqUjhqK6GwrMG+SKFyGkbvdYts1vi3+2\n" + + "/ZJwt0YUQoYG4MIJQZIZv5anTKXZSCmG1SCu+pEGwg0NOtvyhr+Nn8KuAmfG572H\n" + + "Tt/Jy7nLENqTeUy/GtCIRnoD/fP1tvjDDK7Ok2dJUyVgjq1GpT/FMDzK6OPZ+Pvq\n" + + "2vZGvjOCT+g5HE2TXPtHEBGJDot+LzkZWGIcH3acRp7mRioP/kEAYeiiql2aDuHX\n" + + "9ZhTRKhP4GvJ5q/K0OsqGM7Ihn2k69K9XvBMwEsQhlMdNcJc8m0Dn2HFnp6paS6E\n" + + "D7g/Sbkj32gNYEd0mkq7rZf1hKJq29MievQUtT2Z2AsVwo6rF/yr5dy5a2rdbKCe\n" + + "PboNYSPq6M1K8MwZhRpVpefDn6WILxam93W2Kh/GKPX0Zq5jufSVrTrFhl/12P8J\n" + + "VBtQFNnirTb1FoDkQAI01ZeFQ3xKpFE23X2r9usTXsnTe88goS5uQ7pVnF0/IZEd\n" + + "VCwo8WYjaP6jgQrzVE38kIZZHFxkp3TKF7dK0FFckoHRxlKLan9K+YOdKjk6hJck\n" + + "G+USZiRML9pcxWlt2MVqNlQYnZ+/hwzr4GSmhL+XsQhp0Wxxe2nv5lLhzqtxyJS9\n" + + "1xTj7ogLVlYcDmKa0WblZ11fCuPPT9T3K006lfU3zjk6+R8Bu/ed02gd5FU/VCxJ\n" + + "IiYRhKJj9vqgC3yVOKqh+ViwXAnQeBusCOH1LTD+kN75ftSr+86oNz+L/4eoHY7n\n" + + "58v0LkgjoIDfAeQHvHKSf06R7G9Zt2OhtS0d4Usm98PkXU4g4ERes5jdx495xm6f\n" + + "Vd+0sqjZ8Cg0WHfj28WunVsxBT99SGnnaRsB6yIZgo/rIf3epbrtCMcmRHAtCT3P\n" + + "EEw4CPOnMYmMagsUdKNQyZs9BR5pWnQS704XXYWSHm76IUv7JztionxC1cwgJLO+\n" + + "vzDr678kwwsy3b27BF5mehmltlYpw6SgalkYRupOHQGz9nOjDoajUtTA084PR6FG\n" + + "EVSwY5XKgd8xL7aXT0x5FIRESCvvi5fHf2O9vuUT718+Wmay0rvUHzRYKUSG+Dx3\n" + + "op6Kxj9wkHRmgdtcu+fnmKcPnowxBeA7bdFXqQIOzCtuhi1GuBtBwKtFwPKxRHQF\n" + + "dwaWY03wLDl1RsR9EPhm5K5Vrub7nicbqDeypCWh4CiiL8OeWMPLzWlqTwHopA/T\n" + + "ygviwOibJW5kh3+YFhplz6VXFmwH1GOpdJiPEZL5tFHBmpKcKgF2Z5r9Z0xV+S84\n" + + "w6J4AwPhl1FeuMs8aoryjYwEkX7wLoth8IUToYuVVudrStVSKnU1IsJZ6R9+RIkS\n" + + "Wzeb8TtdcxYIx2RrbPK3nlHGsg91TEkU68LDVgeLHVcyLVGCzkyprv9V2R4KG3Zn\n" + + "6OMJ3IqImM3VqFrJAF6TwAzfL6w8imEi1stdUkHqOM9dA4K8xT6Qn8qVRYbHkDEi\n" + + "7sSYmLXRpeusACJ2cd76IlN35YBj4iB9ep0JOIfDcTBgV9r9PwN2R3SuqgK4kxt+\n" + + "rQfH2WjSS2hT8JzGRUyGn9cxMkdQIz3fGlsiERPoxLzVFoEN1NLWBH5QPt94uwf9\n" + + "cIFsM2yw2DX6+U3rLex+UWc9xuxtTw1Lrh6q18AjQbl/0+DgO0GLBW/B74Y8fW5j\n" + + "4dWEMJbDsCk+7WlCZ9nhonmb0VuddKLqZQ4Om+vYthQ/4PzJjPZwx1FzpnowG0oJ\n" + + "Y0lHSsBmiJzgcr9bCR20LOgIh77ND9ZgmthZDWskKvRbyVVniT2oozpcQboDUE1w\n" + + "6/LzfxF750/K79j7q2ZZmEmGgVmBPmb4ssItz92STTzWhFD9pZkxF2jA4EmKx5Lz\n" + + "RSQ8gusaGWNgfHor7aGQkzKM6YhMEDRBhvrjvOQZN9GuLvTTd7mNYfo3PTtAqYLT\n" + + "k5/IsrM9Mn2f3woDKKQ3dX+6wt01el+CBuoMFsOWxXB/c2D16uFmipykR0BtoUAW\n" + + "eGx+r5wgSKnepvSXhbgMqEPXip6kfxK4Y1WfeZ/3ddaOYCbYpNTcs+looYlXDGqL\n" + + "/CAhs4+5lSSZRlq+qpvP/NvpfaC3hsBEDlxBEbpVziDZCtBIyZ8uoT2hU3mUgcy4\n" + + "8fcvMBfMvAMzTecMckuz/VOnmV3zn5BOmaIJZ0rFPa9YeRz+eUYDqO+b9UGl4ifS\n" + + "LtxtDjWGhGEEhl7lhZAJmnYJBgkqQ1CVa2Mu3FziUF7UKy9z+rFZ/djiAxM9sSuh\n" + + "mayukj9+or+1wEK4pYeOlN2Eqivsv66BeVD9hQNEJ/Yq9QOjHFcaRmYY140YfuyN\n" + + "O2gtdA2I5Vk0A5rXf/mRQtaWfGWbje1CLu18F84tLiVpBRx89+ln0hoqKiV4ZhYm\n" + + "yld4AL7sbcGTO3tIai3rEv34KGKY/SUtIRkmJKoKNd9AdM0+j6YRIDZQeT+C3b67\n" + + "ctweDafSNR0fU0M6Ag682HPYtPs7Eo0Vgz92hMKqiY+SXDFoM0Gsr2b7hmv4JhVp\n" + + "ixxuxa2sDwWO9l6XUyZUr14BsTbTZbRZsYfGJAKBiqT5hHCP0n7hHuRUwEmYvtgx\n" + + "K6lXEoKZGCyQLDyliOXNwMHtn2v2SytuwpBkgFhITMIEvslJy0pAK1ZbLdL9JFiN\n" + + "NHfvu5p4TfMw1QuNlGv/oQoFPR2QYP/BSbkR0qU1yKK4vZms0ZcM6/T2tclsithM\n" + + "MBoLvpiHSuYmuQ2ICfZ6zsiYwxXrVIEgEsEw1m5pz1M3uHYyK7JxGz8B8sxGdRJi\n" + + "K8th8rlNml9tkchxl9MdtNq88+SWskOD58FvGhw5+sruXYzx8KKs6J8bS1phLpXw\n" + + "f1Eu1TZwVlgJNppKVPg/cH/jzZYD4lLB/L+fLQ35VtxXlQKp05QSe6lfl+hfvnIr\n" + + "8e7xkrUO1Vpemt6xce2ojNsHqrlY1KNJhGXTiJ6loWvb6Vmc7nwRNGanbK8M1Qid\n" + + "IYCjEbQHlhvHqJJ/8VNf60M3+tOZUuW1CsyO2BKw6aDGiZvypE6rpcUKzsXPSccz\n" + + "LVLNgeljbOejHseAmrvY7uc7oZ+KZFapD3SbLKgVC8yhe+f9pQxZSBIamwIWmHXL\n" + + "LGlJHa5aKmj4bJh7AphKsI9p0opBzw4fiy13mLLPhQIejVioS8xA/qsNGUgJsx78\n" + + "E42vtKHNwYPz6u0FXFm0LxP2tXW6hSwdH+wD4uh9dsuw+K9tn8JD//nz8NY1BJlv\n" + + "H8iL0YdE5489WVKVk0FZAePpy2d2np/PU0JXjk8QTPSu5GOITbfMJDCPKGzpVXzo\n" + + "V2NyUhH2uHEX5uHtbHg9W+LiGkbFGozqjVP0fZYFNdHL/7gk01hOKnK9/a1TXNEd\n" + + "5ekNjl2GfyzPAEZIlQYkTG1TfN/oOBEKe+HrzU3bOIw3KqTtjml5rX9RiEGtSpbw\n" + + "MQDHleaMG8Cuk73wQxRUL1eyDlZ2M/KlocK4ToUjWONwdc+lgdIg1xQNz98a7186\n" + + "BO9E+UQFotkMRrPPzxu/djeghoT2bJtY3S3bsZs5m2yZFpgyGzWEZOJZcB8uf4aO\n" + + "QlfcyVk9l2TcHww5kOWP+X/aKtlLmOv7vRPd16tmIvOLPzN0SAYjNrx7o+NmF/FQ\n" + + "mMVOUsJY397j28eIpvUcw1GgvR4OwkYc1Fof1WUDsxM2EHbgYiSsD8EwpTX1C64y\n" + + "zwqS/A16oDCJ/6yB1l1ga1QwAZam53/hkD251gzwfCvQo4LtVZjBHMbE7pvGM5Qj\n" + + "lglOFE6Fr1MRdtnGVI2uMOxRf5PMO+BwDdQ/QNlfRNAso+BdBZREZJ3t6pkRLEQb\n" + + "/EfYv0gWln/KJiaPwtAQFyN4HzRidXybxRAXUV1jhomeq73ZLTpBSVVkbXeRBA4k\n" + + "OGpth4mN+AUjMjZXAQIHLzBIrNoAAAAAAAcSGyUqMg==\n" + + "-----END CERTIFICATE-----", + "MDQCAQAwCwYJYIZIAWUDBAMSBCKAIBh1QWmkEd9Hv+yNO68PJMXAkn2+h4AN3lYm" + + "czdgQSDY" +), + + EE_MLDSA_44_BY_CA_MLDSA_65( + "ML-DSA", + // ML-DSA-44 + // Validity + // Not Before: Aug 15 02:44:31 2026 PDT + // Not After : Aug 12 02:44:31 2036 PDT + // Subject Key Identifier: + // E5:CE:41:53:0B:C1:A0:97:6B:81:A0:92:F8:36:8E:47:42:7C:50:E9 + "-----BEGIN CERTIFICATE-----\n" + + "MIITJTCCBiKgAwIBAgIJAJhhOXJTIGGtMAsGCWCGSAFlAwQDEjAPMQ0wCwYDVQQD\n" + + "EwRDQTY1MB4XDTI2MDgxNTA5NDQzMVoXDTM2MDgxMjA5NDQzMVowFDESMBAGA1UE\n" + + "AxMJbG9jYWxob3N0MIIFMjALBglghkgBZQMEAxEDggUhAI5Te/gBXtV7TbMPrr1/\n" + + "PQPDLtxayyHRbahDNtruqbJA1ShFCjKQVAdJk0MDe1r6dZc0AzdEh06kD3ZrRT5U\n" + + "9trWqEiujvV9+W2xezeFBubiYCu/nZ3yS7KQwS5p8VYQ3+JZAvLrzPw3azh8jsPy\n" + + "ZvPcl4482MmQvVgpWHX68IoTz9bLa00RaZaUgdRTrJyMsGD/LGs8CNKWh7CVJtvk\n" + + "YYb7Dxh2AQcfyWtWEAgH5BPdlNBLg+mJG4zIneY9Itl1RIsXdIyfwdBuNWsP2wZd\n" + + "xjdRAZD4S4248GmETulnq6GbgcaHK+iOX/+q3HIwEY/Nz0OrECSjmvikGwdbRX+v\n" + + "QZ0VR6b5cNkc1bCKSTDIcOurzepGw8Vrm+1NMAX/q6EbpHslVUUMrnd95aTJ4zep\n" + + "TUwBxB3pb1aQ5mGy5ChtfjlFUR7ObEnVJQthHlSZh7omIDvMCq8rWSZiyeouUpw6\n" + + "jRuOkTeeZeimIBiRTO5j/tgcRQe0wvigZwrSWstMNdpNE1v4pRiwJYfQ8s7KX2Mb\n" + + "k9s5/UGdqRUtH+0TlINc/KLq9eOoYNZcfg9yHKVyYX7WefJz0sgnlb8m/t/TQI8n\n" + + "UXF7UcV/+JFD3L2Ex6IJ1gfNfm8AJDK+8RTHd+vv2pq3RicREazjhdq3Fm0fN/49\n" + + "Y6rKIgMf13YDD4qRC5L4orNi9yjpZB7nd1+BFEJtOwpMyeaJo2uqWUaFQyxKs1uF\n" + + "jfSuwObxWogrgJST5CEyEYKkUQHFnwAaIeXHK6eT6TgBEBViK3Hs7pzTOPUsaYyT\n" + + "J4vqW8ji1aLdmIAIdJrJQ0jVMXk9hqQmOkigkyaNWI6i4CKebT8/+AwEVLmTiTRy\n" + + "Shyn/xwDKOmvpFDIxUIAxBCcLdlpGurfiyv1Zb2Kz4KxOYXp3lt5YMM3lXjqvc/F\n" + + "RObNfYJU5+/J0AZ8mRdhA52VZKQ2LqUcpXbuModrkhz7PsFQLzw+XX53j32cr2cT\n" + + "spc0VUROurWLpk2vJSTOR4QLMOQtxgWgFLjYFNLbA127LXg7r6mO0NVuq9Ey3+ns\n" + + "/NgmrkcKMeBQEHJaWikiDj3hPwgigAAURPPmfRvBcxHC0qXzX7BA2MwP/bk3FKT9\n" + + "JOK/8YQc3Ya69jNLZ7NecHP5xnFOd/yTuES/FoWDKGeDVI59/7unVGzufJyoU3+g\n" + + "FSxSn7kpHvg2iXEXm5TL0R7Ib1BWEoZCQ8dZHxtby20G3NSQZU22djysJqnAHrWC\n" + + "oFNAe7OFvoibVlxsdlfMOXcCmDmppw5+XCoe7qoZ0ThXebaOfSfPzdvFbkBJj0IF\n" + + "alvfnmBLbkLQF4PqKmdAUS40uPm4iVKm4MWDLoZnW7OAQBmy8of9nGqIJTP8vW9Y\n" + + "AcwMYUOdwcgowNY0JxEM+Nh1C4akUicj/bSYOzWM2EKgNqdZrinZasMQpTHNGun/\n" + + "D+vJ4ohOoCiK1uKxHliBnEzX8uxWYVzDelRxCL2xovUT2pWkG56r/JkrjCHXiFrd\n" + + "LNYte2UQ72Q6na/whVbJwpKcyw/jMrzLbQPzwxlBQfJEH0P8qVxsV/11yZC1Qe8d\n" + + "6TB9C7wHhWqhqatlk2DIzHUtBwcrMSZRQkyucgXTxGB1MM+68ItfzzH2QnpSao6t\n" + + "8m2GqQ2zl8Blg/uqU5AqdWNToQ+s/3/QCMqkKU5jhnJg/Rm6xcnCCnRiQSKNFhzQ\n" + + "wKg8J7hByAIIznHt1NG4a0B4d5yY2G8Q4WihrSt2ApC7C9VLtm9qeUWxg5TkKuTD\n" + + "YX2jgYUwgYIwHQYDVR0OBBYEFOXOQVMLwaCXa4Ggkvg2jkdCfFDpMAsGA1UdDwQE\n" + + "AwIHgDAUBgNVHREEDTALgglsb2NhbGhvc3QwHwYDVR0jBBgwFoAUw5tiTF1F5m1u\n" + + "jnzjk+yPDq4I5f0wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAsGCWCG\n" + + "SAFlAwQDEgOCDO4AGlKMxQCLw5WCMGsp5TQKdRd5p6dZGUWejkObDGNeDH8T9Vz0\n" + + "dSIWz4cy5fA0WR/RLlwNtdwREyqqr+m8+DBMZr28XPzq1uFxh4+hPmCgjdyHAdMY\n" + + "CjhsOiRqyS1Fw9/dkOQZ9+qVWEopeYdFHx4xGZO/EAnwfa5yyKv/jbYg8mAG7IL1\n" + + "LRgsoDbl4v/+yRVGkDiY90zPxlQnSYbKgSlnoSuE3rY6ThpEnD0imlbdy8hZG7qB\n" + + "PZjpgdy3oWJUAeiul+W/y/aDAU+oCRRlMX32Xab4uLR/exo3QJN3+eNQOAWDuwXw\n" + + "n33Tfo19nmVIFJKstIzbb2/PeHXn0yvyzO7PXyZHYWRz/NN5K8hU9VuXSdSVEl8S\n" + + "uqBOnynvfuKw3mc/VlvHxIaQPbi45UmfZlQrK++OieH/+c7OjhkLLBi5B9EE88hw\n" + + "6eu4Lo7SpDu0hrJoPjkOUOJiSm9HEREgn4sfJCOFgbRtbqo6kJXqzFth3liavpyo\n" + + "cgC+h8QkstEUhj9hNgGCjTU8YoBJVcpEK/8hVPEk35nSzwxMS0lSshPxZLlxAePf\n" + + "5HY2C5sEU+52dxy4YpqO1tJ4Wvv70jvKf9YGTIytaK5s4MImQsmzQdHYQ3kQTeHU\n" + + "QOnFCB7xn0Y23q8wFy6gNNFHAXDB3QM1kGdflb+cm4GzAxgNlxD1ACXkFOnej+0O\n" + + "wroaHxRIpC92/hVAEkk8h6IhugU9lNtjCQU79vkV3tuHrrG1aKmsldZg3dglYtOK\n" + + "zVg6CRA1Q4V2a6uv3KDbFzcXqb9KvTPTGfiraj0iklsCRpFHSbFpDRoGaS9tqzZy\n" + + "SU2PtD833U9qj5WqURzY33LSArEvZvP81eot7qgDLfe+wMIlKIzC4ZR2GOsgwW63\n" + + "XA4EEGGBcVSIr/NF8EVon7NV9ac5wEpsXIjWLQfAiUlfnvZzvPnBisP/KEgFSq+0\n" + + "MCfTjyf/cLH5QEqTKdqmM3hbZClZDzWPMUhdkwAYArx266k2mnZYxDQ7O9Ypfzku\n" + + "oJUH7veB4D6z0LxIg19NP4nEQ+TdhSbtR9ArawwCM1x3HUuXp1fhZl4sMelAOGDw\n" + + "igyrzAMqpNVoawlcjnp3Enq6D5kkl4a2muW5Em8EqSj9giJsMqZwxX8GS4z1azWA\n" + + "eDvPM9ZPQuixYrFBKZnZDVwWTWXg8Taw5ga0FefUE4akmkPerRdF43WpiZHmMcdV\n" + + "cNXzDSZp+nH4/1lHkvoOSozd330OlktFBAHrdpywuuLCwlhmnR3So45fFmwDR2wO\n" + + "9W4lmoNg5NSThCdBjCHi+ggEkVEe3aZ/4Jp0EjsiqO0ip8v9AbaQu+RLiyhs5TYv\n" + + "qzjLZxkgtdVCdK2KItZVSKDTJGeBUKDaG7KAAPjXDVd3eG4RFlIkGsFmzMbPSSu6\n" + + "435QJHJT0in0pP907kdNsb7BnaYJESCjkb6+u5QLBUYnS8URNuV6aScnS+X2VaaK\n" + + "MYYZHRgHbohjW/V2E1hZa6nEHCDW5wiQIPoRXUo4yjNXF3EzNRklER2Dzx7gSjX4\n" + + "qPm9GsPvTNkMX8C6COTFQ+YP1vppIXaqU+KHOFUMhhgwrMRkovIwXmLE3NMSdn9O\n" + + "9jRPBt3I3d0Bb2wKALILNJ6v5kScQxQIZD2CtghSMYPQXQRaVx/SNeYthVfmUqae\n" + + "S2WErN6owzGqJhw1vFCMdKRuFGR1CHqj8J1CukUUmJ4LZIxGZuxs8jlZQ/P62jXg\n" + + "0fMyhwacvhD80iwBZK+VtAc+p/FkUznqxDRSPapOT3H9sBeXH7UOUq7Dja/iW4uk\n" + + "enoC01b34bpF954cxAHl6FUGNx2FsfMCymmAMoR4NLtmnqDjPfS/C6aXV1OOE7Yj\n" + + "CZAWxsH2qPSKtQfJOBMDGPH1Yq6MTWYCynE9ZSVU3F0vvrnfIeRu5zntJrRziBfd\n" + + "YUi5mRF4HslDV4Fkxcn08xYvunC2jzSTuSH+hUwufY6fvRIXSArPJHn2HcrnpQaY\n" + + "wyepS8Yw50v4wmePS+l1PUZq6KhVrlWk0PlCGQkY5raVWQ3I0Gw+wqJkP4HQ+YOa\n" + + "ivuCrinoNE0n37KKb/x1aIHEow5nEfBqg5s0o2Yfdb3FsKShN60fNKzT7jdabMPg\n" + + "8RlolMKE8lveIjp4WiWiFZ4w3DOnNXMKrLHyjNR/5oOgujPdMVXI46GLyjCNe4b3\n" + + "sYeMnsYYEb01Cg81LeRbzaSM+cqrmgUgFrrPXNJdcNU4AoUqXuSeEWMtRZKuSKkX\n" + + "kooQgSanNcvzvuwqKH3af3UBzcB3Ax/SLTJKjx3sx06AytkgtDvuHXecnTa7GLed\n" + + "tNeShN5R7lGtldnW6XblXWZDdPuz+grNwR+V9b+k0IvjQ+lroT49yrxSs91OD3aI\n" + + "mErqWjRpjK78yLI/GVop+B/UiB+J//TQ5Hc4nwzKjB8iYauBHrIocL5nZpq0BkCj\n" + + "gqZIXxUE9Q9JYPGWQG+J5MVW48A7o6ftZHAn/dhSyQRl2hp1l3WaKDk+4NkgIfTX\n" + + "FDQLOVM4DhxqTFXBzLmLsqBq754vUjjEjEhjmkIjtvNIU2edDZ7hgE2IP98QWlUV\n" + + "kVm7bCQ30FTL4KzVCz9JLo9rDvvy7fWuu/LqZNEVOdgqYY0bTiQPaSrL3gXXk9wV\n" + + "KtVGDXYxYtcZNecdEU+6/tccvxfQVm8yvEk9aFlJn1A8PtBCGyH3/B8nv+1FW4Uq\n" + + "sYF7pZbl9dtVKDMOnDgqehccr1ekxfyjlbI7YS50m/NaC7aIapTJzvEV1wc7rBun\n" + + "n1gtzSYX1U93KY+uGsmyBtdfuScYSCI9BJa+29xQ3UmzE/i1ir29ldLfUiHXIntO\n" + + "ipKozBOzkO/+/aMwD80Ypydfsh/Ni+oSy+sPlPKc9ck1HFsfmsgR9I7F+bVBXw2A\n" + + "DPE4qw/8++y81fHeQqD/mQpcwYFrmIX1uR0tfqF/Nljfhhwgzb63iRNKfi/oxOeY\n" + + "36+buOhFRHbCyUmO+MoDMsNKiivn/FwEFwxPjNoMfANwfzvE3AHtWQMfcF2V/Al5\n" + + "ehC6c0mYZF7ZwKAnwOXH195cIh7zmhjJm+roo3EFC6pQrJrBJdn7CQMe6TazECAs\n" + + "2tAvjcbunWQ/wGOvEZUxk8tKno2lPdMCm9mZvZV2kKXQ44DlTjdfCNafmCBuOm9q\n" + + "DOF6RyEVYDuTEOy5DAsTzxO80jhe9ifQ0krRbQxqMIz1xlG9PtleuaU2vV5WGeDG\n" + + "cFIMdMmVxDNbUJO99IdOgQii4rrwcJkRljbb7H0Xj2eyY1DYCrLhB5U/vf/wrh/t\n" + + "XO9IDwFvl1WBxKXJDY10xBJIEKsfiTEBIWKa2ykW5HfcTyVk2VCxvWBq/CZ5Jt+k\n" + + "MqNB01O3B+/mceMklvpKaA5EfVc1OqeUamcdLyHLbs9TALS+MpVa7UuPFXL9fIkX\n" + + "jmC6ToEX/VReRNVy/xTD8pZxucyt8o4hFffQMX3xVeD9pmQlw0WnhpMQ6Dxb45gX\n" + + "mzMoAYy9Gl9la6pVYNZqrR3KzA49MCsHNyyh8yshzBsnY48wtNLK7t/ciCjwieqF\n" + + "PGLKjGGmvEOswcFDzPw683VV4+q6m1jYjiKnO3FHGdDRll3BBkPmuhaRaef7+V5C\n" + + "/SrybYEzTX/tpfP8cCWLtBKW/+3RGiVeRTbku/mvvq/WMMQeYEbtJeL8HGNMi/In\n" + + "EBDN2TaC6PLHQZ5uHffzFJdUpwHl2kOsSJfY85r8rNWWphP3IYfbO36eOahRl94Z\n" + + "7nZRnbkjsaL3aavTGj0XACSACh5ylqeM57lCGKlUQ1nnxxeJ/MJkFJE+xASXpstN\n" + + "alLg0MVIjqRfQ2JSpdEk1vtPzXMKdHruIIqkSHIOhRUi77TWqMAWUyLL7XvYxSLQ\n" + + "otj6LZD0BD4i7Ran298hHKZg9bc2BoFYabt5uLlS7Xtk87+OEqKRFjTtEzFsvOF5\n" + + "Dk5fYsU/Y7hMdLfvE7wWbqJXFZIuImXyQ8e3HmdptdsZTyCLzgi4dfKOtke118q1\n" + + "elalLve4/KdSS3OCdg2avQFp+yEEAcPyBs9x0KLZOngF/PlEtEty9Pj6ep28bPgp\n" + + "bc2E15p0e2oo9k+yPF646tr8XSyJNTuUokeDyy20TmSZ62Bwqh8zZZ1lGfBWyGKQ\n" + + "ADdoBEAdy6wnKuvKWuRSy4d5JYbTiJwZJ0AkIlcyO7dtdiolNk7kFGcvgT/qFu1K\n" + + "fAjQEE/aiWsQyjzEBQesSCq/1y5Bicfz9Q1M+xfkAk1Ln0WQ7v2Rg7G3/a+GAsjB\n" + + "N1sJA1Uy1wSQiKG3iTWOdsV5vz7F7wyDQRoO1MCgOOs4AggGu+QVd93zR49JHKH9\n" + + "4AwawIdbbV7RgzoqwpJtfuNEEmakDW59YNbHAcjAKy0FGGa4eDWEYXCsOFMtRll5\n" + + "nsrx+xlSaXp7kc7+NH+y0d7i8fT1+wcfJWZqeIyOlHZ6iqi9wP5DZYyl8AAAAAAA\n" + + "AAAACBAaIyov\n" + + "-----END CERTIFICATE-----", + "MDQCAQAwCwYJYIZIAWUDBAMRBCKAIDjWz10RKhoeg68EqDqPEGZarGxNdlYZofe3" + + "re/Bm2RW" +), + EE_EC_RSA_SECP256R1( "EC", // SHA256withRSA, curve secp256r1 diff --git a/test/jdk/javax/print/PrintSEUmlauts/PrintSEUmlauts.java b/test/jdk/javax/print/PrintSEUmlauts/PrintSEUmlauts.java index ee7a983f505b..5677fce94f7b 100644 --- a/test/jdk/javax/print/PrintSEUmlauts/PrintSEUmlauts.java +++ b/test/jdk/javax/print/PrintSEUmlauts/PrintSEUmlauts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ /* * @test * @bug 8067364 + * @requires (os.family == "linux" | os.family == "mac") * @summary Printing to Postscript doesn't support dieresis * @build PrintSEUmlauts * @run main/othervm PrintSEUmlauts diff --git a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java index 0313fcc582d6..072c944e6ae6 100644 --- a/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java +++ b/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java @@ -41,13 +41,10 @@ import jdk.test.lib.containers.docker.DockerTestUtils; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.Asserts; -import jdk.test.lib.Container; public class TestPidsLimit { private static final String imageName = Common.imageName("pids"); - private static final boolean IS_PODMAN = Container.ENGINE_COMMAND.contains("podman"); - private static final int UNLIMITED_PIDS_PODMAN = 0; - private static final int UNLIMITED_PIDS_DOCKER = -1; + private static final int UNLIMITED_PIDS = -1; public static void main(String[] args) throws Exception { DockerTestUtils.checkCanTestDocker(); @@ -109,10 +106,9 @@ private static void testPidsLimit(String pidsLimit) throws Exception { Common.logNewTestCase("testPidsLimit (limit: " + pidsLimit + ")"); DockerRunOptions opts = Common.newOptsShowSettings(imageName); if (pidsLimit.equals("Unlimited")) { - int unlimited = IS_PODMAN ? UNLIMITED_PIDS_PODMAN : UNLIMITED_PIDS_DOCKER; - opts.addDockerOpts("--pids-limit=" + unlimited); + opts.addDockerOpts("--pids-limit=" + UNLIMITED_PIDS); } else { - opts.addDockerOpts("--pids-limit="+pidsLimit); + opts.addDockerOpts("--pids-limit=" + pidsLimit); } OutputAnalyzer out = DockerTestUtils.dockerRunJava(opts); diff --git a/test/jdk/jdk/jfr/api/consumer/TestParseEmptyString.java b/test/jdk/jdk/jfr/api/consumer/TestParseEmptyString.java new file mode 100644 index 000000000000..dc17fd70ff09 --- /dev/null +++ b/test/jdk/jdk/jfr/api/consumer/TestParseEmptyString.java @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer; + +import java.nio.file.Path; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.JVM; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/** + * @test + * @bug 8392325 + * @summary Tests that an empty string is not parsed as null + * @requires vm.hasJFR + * @library /test/lib + * @modules jdk.jfr/jdk.jfr.internal + * @run main/othervm jdk.jfr.api.consumer.TestParseEmptyString + */ +public class TestParseEmptyString { + + public static void main(String[] args) throws Exception { + Path file = Utils.createTempFile("empty-string", ".jfr"); + + try (Recording r = new Recording()) { + r.enable("jdk.ThreadStart"); + r.start(); + + Thread t1 = new Thread(new ThreadGroup(""), ""); + t1.start(); + t1.join(); + + // Flush is needed to make the empty string appear first in the parse order + JVM.flush(); + + Thread t2 = new Thread(new ThreadGroup(""), ""); + t2.start(); + t2.join(); + + r.dump(file); + } + + boolean found = false; + for (RecordedEvent e : RecordingFile.readAllEvents(file)) { + if (e.getEventType().getName().equals("jdk.ThreadStart")) { + RecordedThread t = e.getThread("thread"); + if (t.getJavaName().isEmpty()) { + Asserts.assertEquals("", t.getThreadGroup().getName()); + found = true; + } + } + } + Asserts.assertTrue(found, "There should be a thread with empty name"); + } +} diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestDynamicAnnotation.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestDynamicAnnotation.java index 726ac9070d6e..6d8c1dd3180e 100644 --- a/test/jdk/jdk/jfr/api/metadata/annotations/TestDynamicAnnotation.java +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestDynamicAnnotation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,13 +41,30 @@ public class TestDynamicAnnotation { @MetadataDefinition @interface CustomAnnotation { - String value(); + String stringValue(); int intValue(); } public static void main(String[] args) throws Exception { + testCustomAnnotation(); + testTypeMismatch(); + } + + private static void testTypeMismatch() throws Exception { + Map values = new HashMap<>(); + values.put("stringValue", 1); + values.put("intValue", "text"); + try { + new AnnotationElement(CustomAnnotation.class, values); + throw new Exception("Expected IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // As expected + } + } + + public static void testCustomAnnotation() { Map values = new HashMap<>(); - values.put("value", "MyValue"); + values.put("stringValue", "text"); values.put("intValue", 1); new AnnotationElement(CustomAnnotation.class, values); } diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestGetValues.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestGetValues.java new file mode 100644 index 000000000000..d08534901454 --- /dev/null +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestGetValues.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.api.metadata.annotations; + +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Category; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.api.metadata.annotations.TestGetValues + */ +@MetadataDefinition +@interface Animal { + String value(); +} + +@MetadataDefinition +@interface Elephant { + String color(); + boolean mammal(); +} +public class TestGetValues { + + public static void main(String[] args) throws Exception { + testGetValue(); + testImmutableGetValue(); + testGetValues(); + testImmutableGetValues(); + } + + public static void testGetValue() { + AnnotationElement animal = new AnnotationElement(Animal.class, "tiger"); + Asserts.assertEquals("tiger", animal.getValue("value")); + + AnnotationElement elephant = new AnnotationElement(Elephant.class, Map.of("color", "gray", "mammal", true)); + Asserts.assertEquals("gray", elephant.getValue("color")); + Asserts.assertEquals(true, elephant.getValue("mammal")); + } + + public static void testImmutableGetValue() { + String[] initial = { "tree", "leaf" }; + AnnotationElement a = new AnnotationElement(Category.class, initial); + + // Mutate array after construction + initial[0] = "foo"; + String[] s1 = (String[]) a.getValue("value"); + Asserts.assertEquals(s1[0], "tree"); + + // Mutate returned array + s1[0] = "syntax error"; + String[] s2 = (String[]) a.getValue("value"); + Asserts.assertEquals(s2[0], "tree"); + } + + public static void testGetValues() { + AnnotationElement animal = new AnnotationElement(Animal.class, "tiger"); + Asserts.assertEquals("tiger", animal.getValues().get(0)); + + AnnotationElement elephant = new AnnotationElement(Elephant.class, Map.of("color", "gray", "mammal", true)); + Asserts.assertEquals(getValue(elephant, "color"), "gray"); + Asserts.assertEquals(getValue(elephant, "mammal"), true); + } + + public static void testImmutableGetValues() throws Exception { + String[] initial = { "tree", "leaf" }; + AnnotationElement a = new AnnotationElement(Category.class, initial); + + // Mutate array after construction + initial[0] = "foo"; + String[] s1 = (String[]) a.getValues().get(0); + Asserts.assertEquals(s1[0], "tree"); + + // Mutate returned array + s1[0] = "syntax error"; + String[] s2 = (String[]) a.getValues().get(0); + Asserts.assertEquals(s2[0], "tree"); + + // Mutate returned list + try { + a.getValues().add("modify"); + throw new Exception("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + // as expected + } + } + + private static Object getValue(AnnotationElement a, String name) { + int index = 0; + List values = a.getValues(); + for (ValueDescriptor v : a.getValueDescriptors()) { + if (v.getName().equals(name)) { + return values.get(index); + } + index++; + } + throw new IllegalStateException("No annotation value named " + name); + } +} diff --git a/test/jdk/jdk/jfr/api/metadata/annotations/TestTypesIdentical.java b/test/jdk/jdk/jfr/api/metadata/annotations/TestTypesIdentical.java index 798382815253..6f68184bf6c1 100644 --- a/test/jdk/jdk/jfr/api/metadata/annotations/TestTypesIdentical.java +++ b/test/jdk/jdk/jfr/api/metadata/annotations/TestTypesIdentical.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,11 @@ package jdk.jfr.api.metadata.annotations; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + import jdk.jfr.AnnotationElement; import jdk.jfr.BooleanFlag; import jdk.jfr.Category; @@ -57,7 +62,6 @@ * @run main/othervm jdk.jfr.api.metadata.annotations.TestTypesIdentical */ public class TestTypesIdentical { - @MetadataDefinition @interface CustomAnnotation { String value(); @@ -79,15 +83,30 @@ public static void main(String[] args) throws Exception { assertTypeId(CustomAnnotation.class); } - private static void assertTypeId(Class clz) { - AnnotationElement a1, a2; - try { - a1 = new AnnotationElement(clz, "value"); - a2 = new AnnotationElement(clz, "value2"); - } catch(IllegalArgumentException x) { - a1 = new AnnotationElement(clz); - a2 = new AnnotationElement(clz); - } + private static void assertTypeId(Class clz) throws Exception { + AnnotationElement a1 = new AnnotationElement(clz, createDefaultMap(clz)); + AnnotationElement a2 = new AnnotationElement(clz, createDefaultMap(clz)); Asserts.assertEquals(a1.getTypeId(), a2.getTypeId()); } + + private static Map createDefaultMap(Class annotationClass) throws Exception { + Map map = new HashMap<>(); + for (Method method : annotationClass.getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) { + Class type = method.getReturnType(); + String name = method.getName(); + if (type == String.class) { + map.put(name, "text"); + } else if (type == boolean.class) { + map.put(name, true); + } else if (type == String[].class) { + map.put(name, new String[] { "text" }); + } else { + throw new Exception("Test error. No default value for type " + type.getName()); + } + } + } + return map; + } } diff --git a/test/jdk/jdk/jfr/api/metadata/eventtype/TestGetAnnotationElements.java b/test/jdk/jdk/jfr/api/metadata/eventtype/TestGetAnnotationElements.java index 0ce8767f8c1d..f15be9e677b1 100644 --- a/test/jdk/jdk/jfr/api/metadata/eventtype/TestGetAnnotationElements.java +++ b/test/jdk/jdk/jfr/api/metadata/eventtype/TestGetAnnotationElements.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 @@ -69,7 +69,6 @@ * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetAnnotationElements */ public class TestGetAnnotationElements { - @SuppressWarnings("unchecked") public static void main(String[] args) throws Throwable { Class[] jfrAnnotations = { @@ -83,11 +82,11 @@ public static void main(String[] args) throws Throwable { }; for (Class clz : jfrAnnotations) { - Class annptationClass = (Class) clz; - System.out.println("AnnotationElement: " + annptationClass); - Map values = createValueMapForAnnotation(annptationClass); - List persistableAnnotation = createPersistableAnnotationList(annptationClass); - AnnotationElement ae = new AnnotationElement(annptationClass, values); + Class annotationClass = (Class) clz; + System.out.println("AnnotationElement: " + annotationClass); + Map values = createDefaultMap(annotationClass); + List persistableAnnotation = createPersistableAnnotationList(annotationClass); + AnnotationElement ae = new AnnotationElement(annotationClass, values); List aes = ae.getAnnotationElements(); Asserts.assertEquals(persistableAnnotation.size(), aes.size()); } @@ -131,12 +130,22 @@ private static void assertContainsAnnotation(List aez, Class< Asserts.fail("Class " + clz + " not found in the annotation elements"); } - private static Map createValueMapForAnnotation(Class clz) { + private static Map createDefaultMap(Class annotationClass) throws Exception { Map map = new HashMap<>(); - for (Method method : clz.getDeclaredMethods()) { + for (Method method : annotationClass.getDeclaredMethods()) { int modifiers = method.getModifiers(); if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) { - map.put(method.getName(), "value"); + Class type = method.getReturnType(); + String name = method.getName(); + if (type == String.class) { + map.put(name, "text"); + } else if (type == boolean.class) { + map.put(name, true); + } else if (type == String[].class) { + map.put(name, new String[] { "text" }); + } else { + throw new Exception("Test error. No default value for type " + type.getName()); + } } } return map; diff --git a/test/jdk/jdk/jfr/api/settings/TestCutoff.java b/test/jdk/jdk/jfr/api/settings/TestCutoff.java new file mode 100644 index 000000000000..4be7a9e78387 --- /dev/null +++ b/test/jdk/jdk/jfr/api/settings/TestCutoff.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.api.settings; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.FlightRecorder; +import jdk.jfr.EventType; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests cutoff setting + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.api.settings.TestCutoff + */ +public class TestCutoff { + private static final String OLD_OBJECT_SAMPLE = "jdk.OldObjectSample"; + private static final String ACTIVE_SETTING = "jdk.ActiveSetting"; + + public static void main(String[] args) throws Exception { + testMultipleZeroCutoff(); + } + + public static void testMultipleZeroCutoff() throws Exception { + try (Recording r1 = new Recording()) { + r1.enable(OLD_OBJECT_SAMPLE).with("cutoff", "0 ns"); + r1.start(); + try (Recording r2 = new Recording()) { + // Need to use "0 ms" instead of "0 ns" so SettingControl::combine is invoked + r2.enable(OLD_OBJECT_SAMPLE).with("cutoff", "0 ms"); + r2.enable(ACTIVE_SETTING); + r2.start(); + r2.stop(); + RecordedEvent event = findActiveSettingEvent(r2); + String value = event.getString("value"); + if (!(value.equals("0 ms") || value.equals("0 ns"))) { + throw new Exception("Unexpected combined cutoff setting's value '" + value + "'"); + } + } + } + } + + private static RecordedEvent findActiveSettingEvent(Recording recording) throws Exception { + long activeSetting = findEventType(ACTIVE_SETTING).getId(); + long oldObjectSample = findEventType(OLD_OBJECT_SAMPLE).getId(); + List events = Events.fromRecording(recording); + for (RecordedEvent e : events) { + if (e.getEventType().getId() == activeSetting) { + String name = e.getString("name"); + long id = e.getLong("id"); + if (name.equals("cutoff") && id == oldObjectSample) { + return e; + } + } + } + throw new Exception("Could not find active settings event for cutoff"); + } + + private static EventType findEventType(String name) throws Exception { + for (EventType eventType : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (eventType.getName().equals(name)) { + return eventType; + } + } + throw new Exception("Could not find event type named " + name); + } +} diff --git a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java index 155ece15b86a..034b791b6f8e 100644 --- a/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java +++ b/test/jdk/jdk/jfr/event/metadata/TestLookForUntestedEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ */ package jdk.jfr.event.metadata; -import java.io.IOException; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; @@ -32,7 +31,6 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import jdk.jfr.EventType; import jdk.jfr.Experimental; @@ -119,11 +117,11 @@ private static void lookForEventsNotCoveredByTests() throws Exception { Set eventsNotCoveredByTest = new HashSet<>(jfrEventTypes); Set checkedEvents = new HashSet<>(jfrEventTypes); checkedEvents.addAll(experimentalButTestedEvents); - for (String event : checkedEvents) { - for (Path p : paths) { - if (findStringInFile(p, event)) { + for (Path p : paths) { + List lines = Files.readAllLines(p); + for (String event : checkedEvents) { + if (findStringInLines(lines, event)) { eventsNotCoveredByTest.remove(event); - break; } } } @@ -198,14 +196,8 @@ private static boolean isJavaFile(Path p) { return "java".equals(fileName.substring(i+1)); } - private static boolean findStringInFile(Path p, String searchTerm) throws IOException { - long c = 0; - try (Stream stream = Files.lines(p)) { - c = stream - .filter(line -> line.contains(searchTerm)) - .count(); - } - return (c != 0); + private static boolean findStringInLines(List lines, String searchTerm) { + return lines.stream().filter(line -> line.contains(searchTerm)).count() != 0; } private static void printSetDiff(Set a, Set b, diff --git a/test/jdk/sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java b/test/jdk/sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java index 3a0aa69eb5cf..44b4cb1d8d9b 100644 --- a/test/jdk/sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java +++ b/test/jdk/sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.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 @@ -26,11 +26,11 @@ * @key headful * @bug 6694230 8198613 * @summary Tests that components overriding getInsets paint correctly - * @author Dmitri.Trembovetski@sun.com: area=Graphics * @run main/othervm OverriddenInsetsTest */ import java.awt.Color; +import java.awt.EventQueue; import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsEnvironment; @@ -39,41 +39,70 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.concurrent.CountDownLatch; import javax.imageio.ImageIO; public class OverriddenInsetsTest { - public static final Insets INSETS1 = new Insets(25,25,0,0); public static final Insets INSETS2 = new Insets(100,100,0,0); - static final CountDownLatch lock = new CountDownLatch(1); - static boolean failed = false; - - public static void main(String[] args) { - + public static final int PANEL_WIDTH = 200; + public static final int PANEL_HEIGHT = 200; + static Frame frame; + static TestPanel p; + static Point point; + public static void main(String[] args) throws Exception { if (GraphicsEnvironment.getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration(). - getColorModel().getPixelSize() < 16) + getDefaultScreenDevice().getDefaultConfiguration(). + getColorModel().getPixelSize() < 16) { System.out.println("<16 bit mode detected, test passed"); + return; + } + try { + EventQueue.invokeAndWait(OverriddenInsetsTest::createAndShowGUI); + test(); + } finally { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); } + } - final Frame f = new Frame("OverriddenInsetsTest"); - f.setSize(260,260); + private static void test() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(500); - f.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - f.setVisible(false); - System.exit(0); - } + EventQueue.invokeAndWait(() -> { + point = p.getLocationOnScreen(); }); + BufferedImage bi = robot.createScreenCapture(new Rectangle(point.x, + point.y, PANEL_WIDTH / 2, PANEL_HEIGHT / 2)); + for (int y = 1; y < bi.getHeight() - 1; y++) { + for (int x = 1; x < bi.getWidth() - 1; x++) { + if (bi.getRGB(x, y) != Color.blue.getRGB()) { + System.err.printf("Test failed at %d %d c=%x\n", + x, y, bi.getRGB(x, y)); + String name = "OverriddenInsetsTest_res.png"; + try { + ImageIO.write(bi, "png", new File(name)); + System.out.println("Dumped res to: "+name); + } catch (IOException e) {} + throw new RuntimeException("Test FAILED."); + } + } + } + } - f.setBackground(Color.gray); + private static void createAndShowGUI() { + frame = new Frame("OverriddenInsetsTest"); + frame.setUndecorated(true); + frame.setSize(260,260); + frame.setBackground(Color.gray); Panel p1 = new Panel() { public Insets getInsets() { return INSETS1; @@ -81,70 +110,28 @@ public Insets getInsets() { }; p1.setLayout(null); p1.setSize(250, 250); - - Panel p = new Panel(){ - @Override - public Insets getInsets() { - return INSETS2; - } - - public void paint(Graphics g) { - // make sure Vista is done with its effects - try { - Thread.sleep(2000); - } catch (InterruptedException ex) {} - g.setColor(Color.red); - g.drawRect(0,0,getWidth()-1,getHeight()-1 ); - g.setColor(Color.blue); - g.fillRect(0,0,getWidth()/2,getHeight()/2); - - Point p = getLocationOnScreen(); - try { - Robot r = new Robot(); - BufferedImage bi = - r.createScreenCapture(new - Rectangle(p.x, p.y, getWidth()/2, getHeight()/2)); - for (int y = 0; y < bi.getHeight(); y++) { - for (int x = 0; x < bi.getWidth(); x++) { - if (bi.getRGB(x, y) != Color.blue.getRGB()) { - failed = true; - System.err.printf("Test failed at %d %d c=%x\n", - x, y, bi.getRGB(x, y)); - String name = "OverriddenInsetsTest_res.png"; - try { - ImageIO.write(bi, "png", new File(name)); - System.out.println("Dumped res to: "+name); - } catch (IOException e) {} - return; - } - } - } - } catch (Exception e) { - failed = true; - } finally { - lock.countDown(); - } - } - }; - p.setSize(200, 200); - + p = new TestPanel(); + p.setSize(PANEL_WIDTH, PANEL_HEIGHT); p1.add(p); p.setLocation(50, 50); - f.add(p1); - f.setVisible(true); + frame.add(p1); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } - try { - lock.await(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - if (args.length <= 0 || !"-show".equals(args[0])) { - f.dispose(); + static class TestPanel extends Panel { + @Override + public Insets getInsets() { + return INSETS2; } - if (failed) { - throw new RuntimeException("Test FAILED."); + @Override + public void paint(Graphics g) { + g.setColor(Color.red); + g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); + g.setColor(Color.blue); + g.fillRect(0, 0, getWidth() / 2, getHeight() / 2); } - System.out.println("Test PASSED"); } } diff --git a/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java index df70db86fec7..f61ad86ad10e 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.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 @@ -41,6 +41,9 @@ public abstract class AbstractCheckSignatureSchemes extends SSLEngineTemplate { // Helper map to correlate integral SignatureScheme identifiers to // their IANA string name counterparts. protected static final Map sigSchemeMap = Map.ofEntries( + new SimpleImmutableEntry(0x0904, "mldsa44"), + new SimpleImmutableEntry(0x0905, "mldsa65"), + new SimpleImmutableEntry(0x0906, "mldsa87"), new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"), new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"), new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"), diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DefaultSSLConfigSignatureSchemes.java b/test/jdk/sun/security/ssl/SignatureScheme/DefaultSSLConfigSignatureSchemes.java index 2be745595d85..80de2793a559 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/DefaultSSLConfigSignatureSchemes.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/DefaultSSLConfigSignatureSchemes.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,7 @@ /* * @test - * @bug 8366364 + * @bug 8366364 8381641 * @summary Return enabled signature schemes with * SSLConfiguration#getSSLParameters() call * @library /javax/net/ssl/templates @@ -69,6 +69,9 @@ public class DefaultSSLConfigSignatureSchemes extends SSLEngineTemplate { "rsa_pkcs1_sha256", "rsa_pkcs1_sha384", "rsa_pkcs1_sha512", + "mldsa44", + "mldsa65", + "mldsa87", "dsa_sha256", "ecdsa_sha224", "rsa_sha224", diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MLDSAAllowedInTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/MLDSAAllowedInTLS13.java new file mode 100644 index 000000000000..875d3d483f14 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MLDSAAllowedInTLS13.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8381641 + * @summary Ensure ML-DSA signature schemes are advertised in TLS 1.3 + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm MLDSAAllowedInTLS13 + */ + +import java.util.List; + +public class MLDSAAllowedInTLS13 extends MLDSANotAllowedInTLS12 { + + protected MLDSAAllowedInTLS13() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + new MLDSAAllowedInTLS13().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @Override + protected void checkClientHello() throws Exception { + // Get signature_algorithms extension signature schemes + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + assertMldsa(sigAlgsSS, "ClientHello signature_algorithms extension"); + + // Get signature_algorithms_cert extension signature schemes + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + assertMldsa(sigAlgsCertSS, + "ClientHello signature_algorithms_cert extension"); + } + + // TLS 1.3 CertificateRequest message is encrypted + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MLDSACertSelection.java b/test/jdk/sun/security/ssl/SignatureScheme/MLDSACertSelection.java new file mode 100644 index 000000000000..cadd40d8073f --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MLDSACertSelection.java @@ -0,0 +1,141 @@ +/* + * 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 8381641 + * @summary Test ML-DSA certificate selection and parameter matching + * @library /test/lib + * /javax/net/ssl/templates + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Dtest.case=success65 + * MLDSACertSelection + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa87,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa87,rsa_pkcs1_sha384 + * -Dtest.case=success87 + * MLDSACertSelection + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa44,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa44,rsa_pkcs1_sha384 + * -Dtest.case=fail44 + * MLDSACertSelection + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa44,mldsa65,rsa_pkcs1_sha384 + * -Dtest.case=fallback65 + * MLDSACertSelection + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa44,mldsa65 + * -Djdk.tls.client.SignatureSchemes=mldsa44,mldsa65 + * -Dtest.case=success44 + * MLDSACertSelection + */ + +import static jdk.test.lib.Asserts.assertEquals; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; + +public class MLDSACertSelection extends SSLSocketTemplate { + + private static final String TEST_CASE = System.getProperty("test.case"); + + @Override + protected SSLContext createServerSSLContext() throws Exception { + return switch (TEST_CASE) { + case "success65", "fail44", "fallback65" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_65 }, + getServerContextParameters()); + case "success44" -> + createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + new Cert[] { Cert.EE_MLDSA_44_BY_CA_MLDSA_65 }, + getServerContextParameters()); + case "success87" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_87 }, + getServerContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected SSLContext createClientSSLContext() throws Exception { + return switch (TEST_CASE) { + case "success65", "success87", "fail44", "fallback65" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + null, + getClientContextParameters()); + case "success44" -> + createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + null, + getClientContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + } + + public static void main(String[] args) throws Exception { + boolean expectFail = "fail44".equals(TEST_CASE); + + try { + new MLDSACertSelection().run(); + if (expectFail) { + throw new RuntimeException( + "Expected SSLException was not thrown"); + } + } catch (SSLException e) { + if (!expectFail) { + throw e; + } + assertEquals(e.getMessage(), "(internal_error) No supported " + + "CertificateVerify signature algorithm for ML-DSA key"); + } + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MLDSAClientAuthMismatch.java b/test/jdk/sun/security/ssl/SignatureScheme/MLDSAClientAuthMismatch.java new file mode 100644 index 000000000000..2eac978669e6 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MLDSAClientAuthMismatch.java @@ -0,0 +1,149 @@ +/* + * 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 8381641 + * @summary Test ML-DSA client-authentication certificate selection and + * parameter matching + * @library /javax/net/ssl/templates + * /test/lib + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Dtest.case=success65 + * MLDSAClientAuthMismatch + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa87,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa87,rsa_pkcs1_sha384 + * -Dtest.case=success87 + * MLDSAClientAuthMismatch + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa65,rsa_pkcs1_sha384 + * -Djdk.tls.client.SignatureSchemes=mldsa44,mldsa65,rsa_pkcs1_sha384 + * -Dtest.case=failClientAuthMismatch + * MLDSAClientAuthMismatch + * + * @run main/othervm + * -Djdk.tls.server.SignatureSchemes=mldsa44,mldsa65 + * -Djdk.tls.client.SignatureSchemes=mldsa44,mldsa65 + * -Dtest.case=success44 + * MLDSAClientAuthMismatch + */ + +import static jdk.test.lib.Asserts.assertEquals; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; + +public class MLDSAClientAuthMismatch extends SSLSocketTemplate { + + private static final String TEST_CASE = System.getProperty("test.case"); + + private MLDSAClientAuthMismatch() { + super(true); // server on separate thread, client on current thread + } + + @Override + protected SSLContext createServerSSLContext() throws Exception { + return switch (TEST_CASE) { + case "success65", "failClientAuthMismatch" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_65 }, + getServerContextParameters()); + case "success87" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_87 }, + getServerContextParameters()); + case "success44" -> + createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + new Cert[] { Cert.EE_MLDSA_44_BY_CA_MLDSA_65 }, + getServerContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected SSLContext createClientSSLContext() throws Exception { + return switch (TEST_CASE) { + case "success65" -> createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_65 }, + getClientContextParameters()); + case "success87" -> createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_87 }, + getClientContextParameters()); + case "failClientAuthMismatch" -> createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_44 }, + getClientContextParameters()); + case "success44" -> createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + new Cert[] { Cert.EE_MLDSA_44_BY_CA_MLDSA_65 }, + getClientContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + // require client authentication + socket.setNeedClientAuth(true); + } + + public static void main(String[] args) throws Exception { + boolean expectFail = "failClientAuthMismatch".equals(TEST_CASE); + + try { + new MLDSAClientAuthMismatch().run(); + if (expectFail) { + throw new RuntimeException( + "Expected SSLException was not thrown"); + } + } catch (SSLException e) { + if (!expectFail) { + throw e; + } + + assertEquals(e.getMessage(), "(internal_error) No supported " + + "CertificateVerify signature algorithm for ML-DSA key"); + } + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MLDSANotAllowedInTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/MLDSANotAllowedInTLS12.java new file mode 100644 index 000000000000..9dee30e71229 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MLDSANotAllowedInTLS12.java @@ -0,0 +1,118 @@ +/* + * 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 8381641 + * @summary Test ML-DSA signature scheme advertisement in TLS 1.2 + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm MLDSANotAllowedInTLS12 + */ + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; + +public class MLDSANotAllowedInTLS12 extends AbstractCheckSignatureSchemes { + + private static final List MLDSA_SCHEMES = List.of( + "mldsa65", "mldsa87", "mldsa44"); + + protected MLDSANotAllowedInTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + new MLDSANotAllowedInTLS12().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.2"; + } + + // Run things in TLS handshake order + protected void run() throws Exception { + // Produce ClientHello + clientEngine.wrap(clientOut, cTOs); + cTOs.flip(); + + checkClientHello(); + + // Consume ClientHello + serverEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverEngine); + + // Produce ServerHello and CertificateRequest + serverEngine.wrap(serverOut, sTOc); + sTOc.flip(); + + checkCertificateRequest(); + } + + protected void checkClientHello() throws Exception { + // Get signature_algorithms extension signature schemes + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + assertNoMldsa(sigAlgsSS, + "ClientHello signature_algorithms extension"); + + // Get signature_algorithms_cert extension signature schemes + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + assertNoMldsa(sigAlgsCertSS, + "ClientHello signature_algorithms_cert extension"); + } + + protected void checkCertificateRequest() throws Exception { + // Get CertificateRequest message signature schemes + List certReqSS = getSigSchemesCertReq( + extractHandshakeMsg(sTOc, TLS_HS_CERT_REQ)); + + assertNoMldsa(certReqSS, "TLS 1.2 CertificateRequest"); + } + + protected static void assertNoMldsa(List signatureSchemes, + String messageSource) { + MLDSA_SCHEMES.forEach(signatureScheme -> + assertFalse(signatureSchemes.contains(signatureScheme), + "Signature scheme " + signatureScheme + + " present in " + messageSource)); + } + + protected static void assertMldsa(List signatureSchemes, + String messageSource) { + assertTrue(signatureSchemes.size() >= MLDSA_SCHEMES.size(), + "Not enough signature schemes in " + messageSource); + assertEquals(signatureSchemes.subList(0, MLDSA_SCHEMES.size()), + MLDSA_SCHEMES, "Unexpected ML-DSA signature scheme order in " + + messageSource); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MLDSASigSchemeConstraints.java b/test/jdk/sun/security/ssl/SignatureScheme/MLDSASigSchemeConstraints.java new file mode 100644 index 000000000000..8d6bc1990e1f --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MLDSASigSchemeConstraints.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8381641 + * @summary Test CertificateSignature and HandshakeSignature constraints for + * ML-DSA certificate chains and CertificateVerify signatures + * @library /javax/net/ssl/templates + * /test/lib + * + * @run main/othervm -Dtest.case=successRsaCertSig + * MLDSASigSchemeConstraints + * @run main/othervm -Dtest.case=failRsaCertSig + * MLDSASigSchemeConstraints + * @run main/othervm -Dtest.case=successMldsaCertSig + * MLDSASigSchemeConstraints + * @run main/othervm -Dtest.case=failMldsaCertSig + * MLDSASigSchemeConstraints + * @run main/othervm -Dtest.case=successHandshakeMldsa65Disabled + * MLDSASigSchemeConstraints + * @run main/othervm -Dtest.case=failHandshakeMldsa44Disabled + * MLDSASigSchemeConstraints + */ + +import static jdk.test.lib.Asserts.assertEquals; + +import java.security.Security; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; + +public class MLDSASigSchemeConstraints extends SSLSocketTemplate { + + private static final String TEST_CASE = System.getProperty("test.case"); + + @Override + protected SSLContext createServerSSLContext() throws Exception { + return switch (TEST_CASE) { + case "successRsaCertSig", "failRsaCertSig" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + new Cert[] { Cert.EE_MLDSA_65 }, + getServerContextParameters()); + case "successMldsaCertSig", "failMldsaCertSig", + "successHandshakeMldsa65Disabled", + "failHandshakeMldsa44Disabled" -> + createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + new Cert[] { Cert.EE_MLDSA_44_BY_CA_MLDSA_65 }, + getServerContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected SSLContext createClientSSLContext() throws Exception { + return switch (TEST_CASE) { + case "successRsaCertSig", "failRsaCertSig" -> + createSSLContext( + new Cert[] { Cert.CA_RSA_SHA384_FOR_MLDSA }, + null, + getClientContextParameters()); + case "successMldsaCertSig", "failMldsaCertSig", + "successHandshakeMldsa65Disabled", + "failHandshakeMldsa44Disabled" -> + createSSLContext( + new Cert[] { Cert.CA_MLDSA_65 }, + null, + getClientContextParameters()); + default -> throw new RuntimeException( + "Unknown test case: " + TEST_CASE); + }; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledProtocols(new String[] {"TLSv1.3"}); + } + + public static void main(String[] args) throws Exception { + boolean mldsaCertSig = !TEST_CASE.contains("Rsa"); + boolean expectFail = "failRsaCertSig".equals(TEST_CASE) || + "failMldsaCertSig".equals(TEST_CASE) || + "failHandshakeMldsa44Disabled".equals(TEST_CASE); + + String signatureSchemes = mldsaCertSig + ? "mldsa44,mldsa65" + : "mldsa65,rsa_pkcs1_sha384"; + System.setProperty("jdk.tls.client.SignatureSchemes", signatureSchemes); + System.setProperty("jdk.tls.server.SignatureSchemes", signatureSchemes); + + String disabledAlgorithm = switch (TEST_CASE) { + case "failRsaCertSig" -> + "rsa_pkcs1_sha384 usage certificateSignature"; + case "failMldsaCertSig" -> + "mldsa65 usage certificateSignature"; + case "successHandshakeMldsa65Disabled" -> + "mldsa65 usage HandshakeSignature"; + case "failHandshakeMldsa44Disabled" -> + "mldsa44 usage HandshakeSignature"; + default -> null; + }; + if (disabledAlgorithm != null) { + Security.setProperty("jdk.tls.disabledAlgorithms", + Security.getProperty("jdk.tls.disabledAlgorithms") + + ", " + disabledAlgorithm); + } + + try { + new MLDSASigSchemeConstraints().run(); + if (expectFail) { + throw new RuntimeException( + "Expected SSLException was not thrown"); + } + } catch (SSLException e) { + if (!expectFail) { + throw e; + } + String expectedMessage = "failHandshakeMldsa44Disabled".equals( + TEST_CASE) ? "(internal_error) No supported " + + "CertificateVerify signature algorithm for ML-DSA key" : + "(handshake_failure) No available authentication scheme"; + assertEquals(e.getMessage(), expectedMessage); + } + } +} diff --git a/test/langtools/tools/javac/modules/AutomaticModules.java b/test/langtools/tools/javac/modules/AutomaticModules.java index 21a5d325d39e..84fc222a83b5 100644 --- a/test/langtools/tools/javac/modules/AutomaticModules.java +++ b/test/langtools/tools/javac/modules/AutomaticModules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /** * @test - * @bug 8155026 8178011 8220702 8261625 + * @bug 8155026 8178011 8220702 8261625 8390284 * @summary Test automatic modules * @library /tools/lib * @modules @@ -34,18 +34,26 @@ * @run main AutomaticModules */ +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.jar.Attributes.Name; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; +import javax.lang.model.element.ModuleElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; import toolbox.JarTask; import toolbox.JavacTask; @@ -932,4 +940,215 @@ public void testAutomaticModulePatchingAndAllModulePath(Path base) throws Except .getOutputLines(Task.OutputKind.DIRECT); } + @Test + public void testAugmentAutomaticModuleWithPatch(Path base) throws Exception { + Path modulePath = base.resolve("module-path"); + + Files.createDirectories(modulePath); + + Path automaticSrc = base.resolve("automaticSrc"); + tb.writeJavaFiles(automaticSrc, "package aut1; public class Aut1 { }"); + Path automaticClasses = base.resolve("automaticClasses"); + tb.createDirectories(automaticClasses); + + new JavacTask(tb) + .outdir(automaticClasses) + .files(findJavaFiles(automaticSrc)) + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + Path automaticJar = modulePath.resolve("automatic-1.0.jar"); + + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(automaticJar))) { + out.putNextEntry(new ZipEntry("aut1/Aut1.class")); + Files.copy(automaticClasses.resolve("aut1").resolve("Aut1.class"), out); + } + + Path extraSrc = base.resolve("extraSrc"); + tb.writeJavaFiles(extraSrc, "package aut2; public class Aut2 { }"); + Path extraClasses = base.resolve("extraClasses"); + tb.createDirectories(extraClasses); + + new JavacTask(tb) + .outdir(extraClasses) + .files(findJavaFiles(extraSrc)) + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + Path extraJar = base.resolve("extra.jar"); + + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(extraJar))) { + out.putNextEntry(new ZipEntry("aut2/Aut2.class")); + Files.copy(extraClasses.resolve("aut2").resolve("Aut2.class"), out); + } + + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + package test; + import aut1.Aut1; + import aut2.Aut2; + public class Test { + Aut1 aut1 = new Aut1(); + Aut2 aut2 = new Aut2(); + } + """); + + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + //patch with jar: + new JavacTask(tb) + .options("--module-path", modulePath.toString(), + "--patch-module", "automatic=" + extraJar.toString(), + "--add-modules", "automatic") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + //patch with class directory: + new JavacTask(tb) + .options("--module-path", modulePath.toString(), + "--patch-module", "automatic=" + extraClasses.toString(), + "--add-modules", "automatic") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + //patch with source directory: + new JavacTask(tb) + .options("--module-path", modulePath.toString(), + "--patch-module", "automatic=" + extraSrc.toString(), + "--add-modules", "automatic") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + //with: Automatic-Module-Name: + Manifest man = new Manifest(); + + man.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); + man.getMainAttributes().putValue("Automatic-Module-Name", "ExplicitAutomatic"); + + try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(automaticJar), man)) { + out.putNextEntry(new ZipEntry("aut1/Aut1.class")); + Files.copy(automaticClasses.resolve("aut1").resolve("Aut1.class"), out); + } + + new JavacTask(tb) + .options("--module-path", modulePath.toString(), + "--patch-module", "ExplicitAutomatic=" + extraJar.toString(), + "--add-modules", "ExplicitAutomatic") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + } + + @Test + public void testAugmentAutomaticModuleWithPatchNoDuplication(Path base) throws Exception { + Path modulePath = base.resolve("module-path"); + + Files.createDirectories(modulePath); + + Path automaticSrc = base.resolve("automaticSrc"); + tb.writeJavaFiles(automaticSrc, "package aut; public class Aut1 { }"); + Path automaticClasses = base.resolve("automaticClasses"); + tb.createDirectories(automaticClasses); + + new JavacTask(tb) + .outdir(automaticClasses) + .files(findJavaFiles(automaticSrc)) + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + Path automaticJar = modulePath.resolve("automatic-1.0.jar"); + + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(automaticJar))) { + out.putNextEntry(new ZipEntry("aut/Aut1.class")); + Files.copy(automaticClasses.resolve("aut").resolve("Aut1.class"), out); + } + + Path extraSrc = base.resolve("extraSrc"); + tb.writeJavaFiles(extraSrc, "package aut; public class Aut2 { }"); + Path extraClasses = base.resolve("extraClasses"); + tb.createDirectories(extraClasses); + + new JavacTask(tb) + .outdir(extraClasses) + .files(findJavaFiles(extraSrc)) + .run() + .writeAll() + .getOutput(Task.OutputKind.DIRECT); + + Path extraJar = base.resolve("extra.jar"); + + try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(extraJar))) { + out.putNextEntry(new ZipEntry("aut/Aut2.class")); + Files.copy(extraClasses.resolve("aut").resolve("Aut2.class"), out); + } + + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + package test; + import aut.Aut1; + import aut.Aut2; + public class Test { + Aut1 aut1 = new Aut1(); + Aut2 aut2 = new Aut2(); + } + """); + + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + List exportedPackages = new ArrayList<>(); + new JavacTask(tb) + .options("--module-path", modulePath.toString(), + "--patch-module", "automatic=" + extraJar.toString(), + "--add-modules", "automatic") + .outdir(classes) + .files(findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + ModuleElement automaticModule = + task.getElements() + .getModuleElement("automatic"); + ElementFilter.exportsIn(automaticModule.getDirectives()) + .forEach(d -> exportedPackages.add(d.getPackage() + .getQualifiedName() + .toString())); + } + }); + }) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (exportedPackages.size() != 1 || + !exportedPackages.contains("aut")) { + throw new AssertionError("Unexpected exportedPackages: " + exportedPackages); + } + } + } diff --git a/test/langtools/tools/javac/patterns/GuardsExceptions.java b/test/langtools/tools/javac/patterns/GuardsExceptions.java new file mode 100644 index 000000000000..8e11dee6b3cc --- /dev/null +++ b/test/langtools/tools/javac/patterns/GuardsExceptions.java @@ -0,0 +1,184 @@ +/* + * 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 8390759 + * @summary Check that exceptions thrown from a guard propagate out of the switch. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit GuardsExceptions + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +public class GuardsExceptions { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testUnreportedExceptionReported() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.io.IOException; + + public class Test { + private static boolean guard() throws IOException { + throw new IOException(); + } + + public static void test(Object o) { + switch (o) { + case String s when guard() -> {} + default -> {} + } + } + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:12:37: compiler.err.unreported.exception.need.to.catch.or.throw: java.io.IOException", + "1 error")); + } + + @Test + void testThrownExceptionHandled() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.io.IOException; + + public class Test { + private static boolean guard() throws IOException { + throw new IOException(); + } + + public static void test(Object o) { + try { + switch (o) { + case String s when guard() -> {} + default -> {} + } + } catch (IOException e) {} + } + } + """) + .run() + .writeAll(); + } + + @Test + void testUnreachableStatementReported() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.io.IOException; + + public class Test { + public static void test(Object o) { + switch (o) { + case String s when switch(s.length()) { + case 0 -> { yield false; System.out.println(); } + default -> true; + } -> {} + default -> {} + } + } + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:9:42: compiler.err.unreachable.stmt", + "1 error")); + } + + @Test + void testUnreachableStatementReportedFromSwitchExpression() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + package test; + + import java.io.IOException; + + public class Test { + public static int test(Object o) { + return switch (o) { + case String s when switch(s.length()) { + case 0 -> { yield false; System.out.println(); } + default -> true; + } -> 1; + default -> 0; + }; + } + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:9:42: compiler.err.unreachable.stmt", + "1 error")); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/langtools/tools/javap/PreviewModeTest.java b/test/langtools/tools/javap/PreviewModeTest.java new file mode 100644 index 000000000000..b521e2f3e3b7 --- /dev/null +++ b/test/langtools/tools/javap/PreviewModeTest.java @@ -0,0 +1,81 @@ +/* + * 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 8389654 + * @summary Test --preview-mode option for javap + * @library /tools/lib + * @modules jdk.jdeps/com.sun.tools.javap + * @build toolbox.ToolBox toolbox.JavapTask + * @run main ${test.main.class} + */ + +import java.nio.file.Path; +import java.nio.file.Paths; + +import toolbox.JavapTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class PreviewModeTest extends TestRunner { + ToolBox tb = new ToolBox(); + + PreviewModeTest() { + super(System.err); + } + + public static void main(String... args) throws Exception { + PreviewModeTest tester = new PreviewModeTest(); + tester.runTests(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testSystemPreviewClass(Path base) throws Exception { + String outputHeaderLine = new JavapTask(tb) + .options("--preview-mode", "true", "java.lang.Integer") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT) + .get(1); // Skip "Compiled from" line + if (!outputHeaderLine.contains("final value class java.lang.Integer ")) { + throw new AssertionError(String.format("unexpected output class header line:\n %s", outputHeaderLine)); + } + } + + @Test + public void testFailOnBadArgument(Path base) throws Exception { + Task.Result result = new JavapTask(tb) + .options("--preview-mode", "java.lang.Integer") + .run(Task.Expect.FAIL); + String output = result.getOutput(Task.OutputKind.DIRECT); + if (!output.contains("invalid use of option: --preview-mode")) { + throw new AssertionError(String.format("unexpected output:\n %s", output)); + } + } +} diff --git a/test/lib/native/testlib_threads.hpp b/test/lib/native/testlib_threads.hpp index 3a7b18eccf6e..0f1e92265ed4 100644 --- a/test/lib/native/testlib_threads.hpp +++ b/test/lib/native/testlib_threads.hpp @@ -28,6 +28,7 @@ #include #include +#include #ifdef _WIN32 #include @@ -36,67 +37,99 @@ #include #endif -extern "C" { - -typedef void(*PROCEDURE)(void*); - -struct Helper { - PROCEDURE proc; - void* context; -}; - static void fatal(const char* message, int code) { fputs(message, stderr); // exit the test with a non-zero exit code to avoid accidental false positives exit(code); } -// Adapt from the callback type the OS API expects to -// our OS-independent PROCEDURE type. +class TestThread { +public: + using proc_t = void(*)(void*); +private: #ifdef _WIN32 -DWORD WINAPI procedure(_In_ LPVOID ctxt) { + using thread_t = HANDLE; #else -void* procedure(void* ctxt) { + using thread_t = pthread_t; #endif - Helper* helper = (Helper*)ctxt; - helper->proc(helper->context); - return 0; -} -// Run 'proc' in a newly started thread, passing 'context' to it -// as an argument, and then join that thread. -void run_in_new_thread_and_join(PROCEDURE proc, void* context) { - struct Helper helper; - helper.proc = proc; - helper.context = context; + thread_t _thread; + + struct CallbackData { + proc_t _proc; + void* _context; + CallbackData(proc_t proc, void* context) + : _proc(proc), _context(context) {} + }; + // Keep the data the callback needs out-of-line + // so that if this TestThread object is copied, + // the pointer remains valid + std::shared_ptr _data; + + TestThread(proc_t proc, void* context) + : _data(std::make_shared(proc, context)) { #ifdef _WIN32 - HANDLE thread = CreateThread(nullptr, 0, procedure, &helper, 0, nullptr); - if (thread == nullptr) { - fatal("failed to create thread", GetLastError()); + _thread = CreateThread(nullptr, 0, TestThread::procedure, _data.get(), 0, nullptr); + if (_thread == nullptr) { + fatal("failed to create thread", GetLastError()); + } +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + size_t stack_size = 0x100000; + pthread_attr_setstacksize(&attr, stack_size); + int result = pthread_create(&_thread, &attr, TestThread::procedure, _data.get()); + if (result != 0) { + fatal("failed to create thread", result); + } + pthread_attr_destroy(&attr); +#endif } - if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { - // Should be WAIT_FAILED, since this is not a mutex, and - // we set no timeout. - fatal("failed to join thread", GetLastError()); +public: + TestThread() { } + + static TestThread start(proc_t proc, void* context) { + return TestThread(proc, context); } + + void join() const { + if (_data.get() == nullptr) { + fatal("Joining TestThread that is not initialized", 1); + } +#ifdef _WIN32 + if (WaitForSingleObject(_thread, INFINITE) != WAIT_OBJECT_0) { + // Should be WAIT_FAILED, since this is not a mutex, and + // we set no timeout. + fatal("failed to join thread", GetLastError()); + } #else - pthread_t thread; - pthread_attr_t attr; - pthread_attr_init(&attr); - size_t stack_size = 0x100000; - pthread_attr_setstacksize(&attr, stack_size); - int result = pthread_create(&thread, &attr, procedure, &helper); - if (result != 0) { - fatal("failed to create thread", result); + int result = pthread_join(_thread, nullptr); + if (result != 0) { + fatal("failed to join thread", result); + } +#endif } - pthread_attr_destroy(&attr); - result = pthread_join(thread, nullptr); - if (result != 0) { - fatal("failed to join thread", result); + +private: + // Adapt from the callback type the OS API expects to + // our OS-independent PROCEDURE type. + static + #ifdef _WIN32 + DWORD WINAPI procedure(_In_ LPVOID ctxt) { + #else + void* procedure(void* ctxt) { + #endif + CallbackData* data = (CallbackData*)ctxt; + data->_proc(data->_context); + return 0; } -#endif -} +}; +// Run 'proc' in a newly started thread, passing 'context' to it +// as an argument, and then join that thread. +void run_in_new_thread_and_join(TestThread::proc_t proc, void* context) { + TestThread thread = TestThread::start(proc, context); + thread.join(); } #endif // TEST_LIB_NATIVE_THREAD_HPP