diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 90d5d7956264..28aea489f7ec 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -174,18 +174,6 @@ AC_DEFUN([PLATFORM_EXTRACT_VARS_FROM_CPU], VAR_CPU_BITS=64 VAR_CPU_ENDIAN=big ;; - sparc) - VAR_CPU=sparc - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=32 - VAR_CPU_ENDIAN=big - ;; - sparcv9|sparc64) - VAR_CPU=sparcv9 - VAR_CPU_ARCH=sparc - VAR_CPU_BITS=64 - VAR_CPU_ENDIAN=big - ;; *) AC_MSG_ERROR([unsupported cpu $1]) ;; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index ed321ca47595..f4fd8ad0dca5 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -85,26 +85,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, rscratch1, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -172,9 +162,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, if (expand_call) { assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); } __ pop(saved, sp); @@ -358,20 +348,20 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(/*strip_ret_addr*/true); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - rthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + rthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ lsr(obj, obj, CardTable::card_shift()); @@ -394,13 +384,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.index() == noreg && dst.offset() == 0) { if (dst.base() != tmp3) { __ mov(tmp3, dst.base()); @@ -409,20 +399,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ lea(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - rthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + rthread /* thread */, + tmp1 /* tmp */, + rscratch1 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } @@ -753,7 +749,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ bind(runtime); __ push_call_clobbered_registers(); __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); __ pop_call_clobbered_registers(); __ bind(done); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index c89847b9d52c..ae607d5c63c9 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -40,23 +40,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index b7156144d8bb..812239bbf7f8 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -50,14 +50,14 @@ #define __ masm-> -void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level) { if (ShenandoahSATBBarrier) { - __ block_comment("satb_write_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - __ block_comment("} satb_write_barrier (shenandoahgc)"); + __ block_comment("satb_barrier (shenandoahgc) {"); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -198,11 +198,12 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec // In "load mode", this register acts as a temporary register and must // thus not be 'noreg'. In "preloaded mode", its content will be sustained. // tmp1/tmp2: Temporary registers, one of which must be non-volatile in "preloaded mode". -void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); Label skip_barrier; @@ -311,7 +312,7 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm } // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, R16_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); // Restore to-be-preserved registers. if (!preserve_gp_registers && preloaded_mode && pre_val->is_volatile()) { @@ -574,13 +575,13 @@ void ShenandoahBarrierSetAssembler::load_at( if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { if (ShenandoahSATBBarrier) { __ block_comment("keep_alive_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); + satb_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); __ block_comment("} keep_alive_barrier (shenandoahgc)"); } } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); assert_different_registers(base, tmp, R0); @@ -603,21 +604,33 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet Register base, RegisterOrConstant ind_or_offs, Register val, Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level) { - if (is_reference_type(type)) { - if (ShenandoahSATBBarrier) { - satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - } + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, + base, ind_or_offs, + val, + tmp1, tmp2, tmp3, + preservation_level); + return; } + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, val, tmp1, tmp2, tmp3, preservation_level); - // No need for post barrier if storing null - if (ShenandoahCardBarrier && is_reference_type(type) && val != noreg) { - store_check(masm, base, ind_or_offs, tmp1); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, base, ind_or_offs, tmp1); } } @@ -855,13 +868,11 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble Register tmp2 = stub->tmp2()->as_register(); assert_different_registers(addr, res, tmp1, tmp2); -#ifdef ASSERT - // Ensure that 'res' is 'R3_ARG1' and contains the same value as 'obj' to reduce the number of required - // copy instructions. assert(R3_RET == res, "res must be r3"); - __ cmpd(CR0, res, obj); - __ asm_assert_eq("result register must contain the reference stored in obj"); -#endif + + if (res != obj) { + __ mr(res, obj); + } DecoratorSet decorators = stub->decorators(); @@ -966,7 +977,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ push_frame_reg_args(nbytes_save, R11_tmp1); // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), R0_pre_val, R16_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), R0_pre_val); // Restore to-be-preserved registers. __ pop_frame(); @@ -996,7 +1007,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_s __ save_volatile_gprs(R1_SP, -nbytes_save, true, false); // Load arguments from stack. - // No load required, as assured by assertions in 'ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub'. + // No load required, as caller has already loaded obj into R3. Register R3_obj = R3_ARG1; Register R4_load_addr = R4_ARG2; __ ld(R4_load_addr, -8, R1_SP); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index b058dcf1a2ea..52615a740af0 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -45,15 +45,15 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: /* ==== Actual barrier implementations ==== */ - void satb_write_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level); - void store_check(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp); + void card_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp); void load_reference_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, @@ -85,10 +85,10 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { #endif /* ==== Available barriers (facades of the actual implementations) ==== */ - void satb_write_barrier(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index f936ae9ba27b..3b5ecd60cabe 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -88,26 +88,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, t0, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -172,9 +162,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, // expand_call should be passed true. if (expand_call) { assert(pre_val != c_rarg1, "smashed arg"); - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); } __ pop_reg(saved, sp); @@ -376,21 +366,21 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - xthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + xthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { - assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ srli(obj, obj, CardTable::card_shift()); @@ -413,13 +403,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.offset() == 0) { if (dst.base() != tmp3) { __ mv(tmp3, dst.base()); @@ -428,20 +418,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ la(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - xthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + xthread /* thread */, + tmp1 /* tmp */, + t0 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } @@ -702,7 +698,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ bind(runtime); __ push_call_clobbered_registers(); __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), pre_val, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); __ pop_call_clobbered_registers(); __ bind(done); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index 3fe7c8d17400..8aef89723ea6 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -41,23 +41,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); - - void store_check(MacroAssembler* masm, Register obj); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); + + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 4e3d30c40818..531732883e2f 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1784,12 +1784,12 @@ void MacroAssembler::vector_update_crc32(Register crc, Register buf, Register le mv(tmp5, 0xff); if (MaxVectorSize == 16) { - vsetivli(zr, N, Assembler::e32, Assembler::m4, Assembler::ma, Assembler::ta); + vsetivli(zr, N, Assembler::e32, Assembler::m4, Assembler::mu, Assembler::tu); } else if (MaxVectorSize == 32) { - vsetivli(zr, N, Assembler::e32, Assembler::m2, Assembler::ma, Assembler::ta); + vsetivli(zr, N, Assembler::e32, Assembler::m2, Assembler::mu, Assembler::tu); } else { assert(MaxVectorSize > 32, "sanity"); - vsetivli(zr, N, Assembler::e32, Assembler::m1, Assembler::ma, Assembler::ta); + vsetivli(zr, N, Assembler::e32, Assembler::m1, Assembler::mu, Assembler::tu); } vmv_v_x(vcrc, zr); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index d043c8af68a4..97829a10a3b5 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -174,24 +174,14 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, tmp, tosca_live, expand_call); - } -} - -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -276,9 +266,9 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, __ mov(c_rarg1, thread); } // Already moved pre_val into c_rarg0 above - __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), 2); + __ MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), 1); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), c_rarg0, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0); } // save the live input values @@ -533,18 +523,18 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d assert_different_registers(dst, tmp1, r15_thread); // Generate the SATB pre-barrier code to log the value of // the referent field in an SATB buffer. - shenandoah_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - tmp1 /* tmp */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + tmp1 /* tmp */, + true /* tosca_live */, + true /* expand_call */); restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); // Does a store check for the oop in register obj. The content of @@ -575,41 +565,40 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - bool in_heap = (decorators & IN_HEAP) != 0; - bool as_normal = (decorators & AS_NORMAL) != 0; - if (on_oop && in_heap) { - bool needs_pre_barrier = as_normal; - - // flatten object address if needed - // We do it regardless of precise because we need the registers - if (dst.index() == noreg && dst.disp() == 0) { - if (dst.base() != tmp1) { - __ movptr(tmp1, dst.base()); - } - } else { - __ lea(tmp1, dst); + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + return; + } + + // Flatten object address right away for simplicity: likely needed by barriers + assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); + if (dst.index() == noreg && dst.disp() == 0) { + if (dst.base() != tmp1) { + __ movptr(tmp1, dst.base()); } + } else { + __ lea(tmp1, dst); + } - assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); + bool storing_non_null = (val != noreg); - if (needs_pre_barrier) { - shenandoah_write_barrier_pre(masm /*masm*/, - tmp1 /* obj */, - tmp2 /* pre_val */, - tmp3 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); - } + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp1 /* obj */, + tmp2 /* pre_val */, + tmp3 /* tmp */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - if (val != noreg) { - if (ShenandoahCardBarrier) { - store_check(masm, tmp1); - } - } - } else { - BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + // Store! + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); + + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp1); } } @@ -946,7 +935,7 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss // load the pre-value __ load_parameter(0, rcx); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), rcx, thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), rcx); __ restore_live_registers(true); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index b0185f2dbffb..b5cc5c8d8345 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -41,21 +41,14 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); - - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 95c0ac0c758f..c9dfe989e670 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp @@ -1027,14 +1027,12 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen, void StubGenerator::generate_kyber_stubs() { // Generate Kyber intrinsics code if (UseKyberIntrinsics) { - if (VM_Version::supports_evex()) { - StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); - StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); - StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); - StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); - StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); - StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); - StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); - } + StubRoutines::_kyberNtt = generate_kyberNtt_avx512(this, _masm); + StubRoutines::_kyberInverseNtt = generate_kyberInverseNtt_avx512(this, _masm); + StubRoutines::_kyberNttMult = generate_kyberNttMult_avx512(this, _masm); + StubRoutines::_kyberAddPoly_2 = generate_kyberAddPoly_2_avx512(this, _masm); + StubRoutines::_kyberAddPoly_3 = generate_kyberAddPoly_3_avx512(this, _masm); + StubRoutines::_kyber12To16 = generate_kyber12To16_avx512(this, _masm); + StubRoutines::_kyberBarrettReduce = generate_kyberBarrettReduce_avx512(this, _masm); } } diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 32675a12cb07..5b9288adebf1 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1273,7 +1273,7 @@ void VM_Version::get_processor_features() { // Kyber Intrinsics // Currently we only have them for AVX512 #ifdef _LP64 - if (supports_evex() && supports_avx512bw()) { + if (supports_avx512vlbw()) { if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) { UseKyberIntrinsics = true; } diff --git a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp index 4d813cd53c69..4c73368b6730 100644 --- a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp +++ b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp @@ -26,6 +26,8 @@ #ifndef CPU_ZERO_BYTECODEINTERPRETER_ZERO_INLINE_HPP #define CPU_ZERO_BYTECODEINTERPRETER_ZERO_INLINE_HPP +#include "sanitizers/ub.hpp" + // Inline interpreter functions for zero inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { @@ -40,6 +42,7 @@ inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } +ATTRIBUTE_NO_UBSAN // IEEE-754 division by zero is well-defined inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } @@ -68,7 +71,7 @@ inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], } inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { - return op1 + op2; + return java_add(op1, op2); } inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { @@ -82,7 +85,7 @@ inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { } inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { - return op1 * op2; + return java_multiply(op1, op2); } inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { @@ -90,7 +93,7 @@ inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { } inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { - return op1 - op2; + return java_subtract(op1, op2); } inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { @@ -104,19 +107,19 @@ inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { } inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { - return ((unsigned long long) op1) >> (op2 & 0x3F); + return java_shift_right_unsigned(op1, op2); } inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { - return op1 >> (op2 & 0x3F); + return java_shift_right(op1, op2); } inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { - return op1 << (op2 & 0x3F); + return java_shift_left(op1, op2); } inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { - return -op; + return java_negate(op); } inline jlong BytecodeInterpreter::VMlongNot(jlong op) { @@ -183,8 +186,8 @@ inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { return op1 + op2; } +ATTRIBUTE_NO_UBSAN // IEEE-754 division by zero is well-defined inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { - // Divide by zero... QQQ return op1 / op2; } @@ -228,7 +231,7 @@ inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { // Integer Arithmetic inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { - return op1 + op2; + return java_add(op1, op2); } inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { @@ -242,11 +245,11 @@ inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { } inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { - return op1 * op2; + return java_multiply(op1, op2); } inline jint BytecodeInterpreter::VMintNeg(jint op) { - return -op; + return java_negate(op); } inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { @@ -260,19 +263,19 @@ inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { } inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { - return op1 << (op2 & 0x1F); + return java_shift_left(op1, op2); } inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { - return op1 >> (op2 & 0x1F); + return java_shift_right(op1, op2); } inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { - return op1 - op2; + return java_subtract(op1, op2); } inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { - return ((juint) op1) >> (op2 & 0x1F); + return java_shift_right_unsigned(op1, op2); } inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index 029ccbded136..38051d6d1e2b 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -385,12 +385,15 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) { goto unlock_unwind_and_return; void **arguments; - void *mirror; { + // These locals must remain on stack until call completes + void *mirror; + void *env; + { arguments = (void **) stack->alloc(handler->argument_count() * sizeof(void **)); void **dst = arguments; - void *env = thread->jni_environment(); + env = thread->jni_environment(); *(dst++) = &env; if (method->is_static()) { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index e4b8c9a970a0..4469e7d26930 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4440,13 +4440,6 @@ OSReturn os::get_native_priority(const Thread* const thread, return (*priority_ptr != -1 || errno == 0 ? OS_OK : OS_ERR); } -// This is the fastest way to get thread cpu time on Linux. -// Returns cpu time (user+sys) for any thread, not only for current. -// POSIX compliant clocks are implemented in the kernels 2.6.16+. -// It might work on 2.6.10+ with a special kernel/glibc patch. -// For reference, please, see IEEE Std 1003.1-2004: -// http://www.unix.org/single_unix_specification - jlong os::Linux::thread_cpu_time(clockid_t clockid) { struct timespec tp; int status = clock_gettime(clockid, &tp); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 859916da9d29..5990e2c4d6d9 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -1336,7 +1336,7 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry); #endif #if INCLUDE_SHENANDOAHGC - SET_ADDRESS(_extrs, ShenandoahRuntime::write_ref_field_pre); + SET_ADDRESS(_extrs, ShenandoahRuntime::write_barrier_pre); SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom); SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); #endif diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 9f58016a6f14..3df3c65d0813 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -133,7 +133,6 @@ LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, L addr = ensure_in_register(gen, addr, T_ADDRESS); assert(addr->is_register(), "must be a register at this point"); LIR_Opr result = gen->result_register_for(obj->value_type()); - __ move(obj, result); LIR_Opr tmp1 = gen->new_register(T_ADDRESS); LIR_Opr tmp2 = gen->new_register(T_ADDRESS); @@ -164,6 +163,11 @@ LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, L CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, decorators); __ branch(lir_cond_notEqual, slow); + + // No barrier is needed, move obj to result now. + __ move(obj, result); + + // Slow-path re-enters here with result set. __ branch_destination(slow->continuation()); return result; diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 1b4f2c79bd25..e36af2b5a071 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -127,6 +127,7 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { visitor->do_input(_addr); visitor->do_temp(_addr); visitor->do_temp(_result); + visitor->do_output(_result); visitor->do_temp(_tmp1); visitor->do_temp(_tmp2); } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index f12b3dc5fa84..fdfde866cd72 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -250,9 +250,8 @@ void ShenandoahBarrierSetC2::satb_write_barrier_pre(GraphKit* kit, } __ else_(); { // logging buffer is full, call the runtime - const TypeFunc *tf = ShenandoahBarrierSetC2::write_ref_field_pre_Type(); - __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre), "shenandoah_wb_pre", - pre_val, tls); + const TypeFunc *tf = ShenandoahBarrierSetC2::write_barrier_pre_Type(); + __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), "shenandoah_wb_pre", pre_val); } __ end_if(); // (!index) } __ end_if(); // (pre_val != nullptr) } __ end_if(); // (!marking) @@ -270,7 +269,7 @@ void ShenandoahBarrierSetC2::satb_write_barrier_pre(GraphKit* kit, bool ShenandoahBarrierSetC2::is_shenandoah_wb_pre_call(Node* call) { return call->is_CallLeaf() && - call->as_CallLeaf()->entry_point() == CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre); + call->as_CallLeaf()->entry_point() == CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre); } bool ShenandoahBarrierSetC2::is_shenandoah_clone_call(Node* call) { @@ -520,11 +519,10 @@ void ShenandoahBarrierSetC2::post_barrier(GraphKit* kit, #undef __ -const TypeFunc* ShenandoahBarrierSetC2::write_ref_field_pre_Type() { - const Type **fields = TypeTuple::fields(2); +const TypeFunc* ShenandoahBarrierSetC2::write_barrier_pre_Type() { + const Type **fields = TypeTuple::fields(1); fields[TypeFunc::Parms+0] = TypeInstPtr::NOTNULL; // original field value - fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL; // thread - const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+2, fields); + const TypeTuple *domain = TypeTuple::make(TypeFunc::Parms+1, fields); // create result type (range) fields = TypeTuple::fields(0); @@ -1108,7 +1106,7 @@ void ShenandoahBarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase p Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_reshape) const { if (is_shenandoah_wb_pre_call(n)) { - uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_Type()->domain()->cnt(); + uint cnt = ShenandoahBarrierSetC2::write_barrier_pre_Type()->domain()->cnt(); if (n->req() > cnt) { Node* addp = n->in(cnt); if (has_only_shenandoah_wb_pre_uses(addp)) { @@ -1194,7 +1192,7 @@ bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, ui assert (n->is_Call(), ""); CallNode *call = n->as_Call(); if (ShenandoahBarrierSetC2::is_shenandoah_wb_pre_call(call)) { - uint cnt = ShenandoahBarrierSetC2::write_ref_field_pre_Type()->domain()->cnt(); + uint cnt = ShenandoahBarrierSetC2::write_barrier_pre_Type()->domain()->cnt(); if (call->req() > cnt) { assert(call->req() == cnt + 1, "only one extra input"); Node *addp = call->in(cnt); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 5bf549203ea9..dd9e9bcc1a5f 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -103,7 +103,7 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { ShenandoahBarrierSetC2State* state() const; - static const TypeFunc* write_ref_field_pre_Type(); + static const TypeFunc* write_barrier_pre_Type(); static const TypeFunc* clone_barrier_Type(); static const TypeFunc* load_reference_barrier_Type(); virtual bool has_load_barrier_nodes() const { return true; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index f6733d4a923d..8537aba6e314 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -72,21 +72,33 @@ void ShenandoahBarrierSet::print_on(outputStream* st) const { bool ShenandoahBarrierSet::need_load_reference_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahLoadRefBarrier) return false; - // Only needed for references return is_reference_type(type); } bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahSATBBarrier) return false; - // Only needed for references if (!is_reference_type(type)) return false; - bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0; bool unknown = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool on_weak_ref = (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) != 0; return (on_weak_ref || unknown) && keep_alive; } +bool ShenandoahBarrierSet::need_satb_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahSATBBarrier) return false; + if (!is_reference_type(type)) return false; + bool as_normal = (decorators & AS_NORMAL) != 0; + bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + return as_normal && !dest_uninitialized; +} + +bool ShenandoahBarrierSet::need_card_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahCardBarrier) return false; + if (!is_reference_type(type)) return false; + bool in_heap = (decorators & IN_HEAP) != 0; + return in_heap; +} + void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) { #if COMPILER2_OR_JVMCI if (ReduceInitialCardMarks && ShenandoahCardBarrier && !ShenandoahHeap::heap()->is_in_young(new_obj)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 0d38cc757f44..aee33efeb577 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -60,6 +60,8 @@ class ShenandoahBarrierSet: public BarrierSet { static bool need_load_reference_barrier(DecoratorSet decorators, BasicType type); static bool need_keep_alive_barrier(DecoratorSet decorators, BasicType type); + static bool need_satb_barrier(DecoratorSet decorators, BasicType type); + static bool need_card_barrier(DecoratorSet decorators, BasicType type); static bool is_strong_access(DecoratorSet decorators) { return (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) == 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp index 9c84ac41e841..4c702e7ddfc1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.cpp @@ -184,3 +184,7 @@ void ShenandoahEvacOOMHandler::clear() { _threads_in_evac[i].clear(); } } + +bool ShenandoahEvacOOMHandler::is_active() { + return ShenandoahThreadLocalData::evac_oom_scope_level(Thread::current()) > 0; +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.hpp index dd77f6216e06..0524ecbb7299 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacOOMHandler.hpp @@ -146,6 +146,11 @@ class ShenandoahEvacOOMHandler { void clear(); + /** + * Returns true if current thread is in evacuation OOM protocol. + */ + static bool is_active(); + private: // Register/Unregister thread to evacuation OOM protocol void register_thread(Thread* t); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 3f0db4fb8eab..a228595ffdf8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -80,7 +80,6 @@ void ShenandoahGenerationalEvacuationTask::do_work() { promote_regions(); } else { assert(!ShenandoahHeap::heap()->collection_set()->is_empty(), "Should have a collection set here"); - ShenandoahEvacOOMScope oom_evac_scope; evacuate_and_promote_regions(); } } @@ -124,6 +123,7 @@ void ShenandoahGenerationalEvacuationTask::evacuate_and_promote_regions() { if (r->is_cset()) { assert(r->has_live(), "Region %zu should have been reclaimed early", r->index()); + ShenandoahEvacOOMScope oom_evac_scope; _heap->marked_object_iterate(r, &cl); if (ShenandoahPacing) { _heap->pacer()->report_evac(r->used() >> LogHeapWordSize); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c6e529fcaa2d..574959428b5b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1190,11 +1190,9 @@ class ShenandoahEvacuationTask : public WorkerTask { if (_concurrent) { ShenandoahConcurrentWorkerSession worker_session(worker_id); ShenandoahSuspendibleThreadSetJoiner stsj; - ShenandoahEvacOOMScope oom_evac_scope; do_work(); } else { ShenandoahParallelWorkerSession worker_session(worker_id); - ShenandoahEvacOOMScope oom_evac_scope; do_work(); } } @@ -1205,7 +1203,10 @@ class ShenandoahEvacuationTask : public WorkerTask { ShenandoahHeapRegion* r; while ((r =_cs->claim_next()) != nullptr) { assert(r->has_live(), "Region %zu should have been reclaimed early", r->index()); - _sh->marked_object_iterate(r, &cl); + { + ShenandoahEvacOOMScope oom_evac_scope; + _sh->marked_object_iterate(r, &cl); + } if (ShenandoahPacing) { _sh->pacer()->report_evac(r->used() >> LogHeapWordSize); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 08c0ae6a6233..cf07fdc45338 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -258,6 +258,7 @@ inline bool ShenandoahHeap::cancelled_gc() const { inline bool ShenandoahHeap::check_cancelled_gc_and_yield(bool sts_active) { if (sts_active && !cancelled_gc()) { + assert(!ShenandoahEvacOOMHandler::is_active(), "Potential deadlock: cannot yield while OOM evac handler is active"); if (SuspendibleThreadSet::should_yield()) { SuspendibleThreadSet::yield(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 97ba5012efa5..0bee8b4cf420 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -38,20 +38,16 @@ JRT_LEAF(void, ShenandoahRuntime::arraycopy_barrier_narrow_oop(narrowOop* src, n ShenandoahBarrierSet::barrier_set()->arraycopy_barrier(src, dst, length); JRT_END -JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre(oopDesc * orig, JavaThread * thread)) - assert(thread == JavaThread::current(), "pre-condition"); +JRT_LEAF(void, ShenandoahRuntime::write_barrier_pre(oopDesc* orig)) assert(orig != nullptr, "should be optimized out"); shenandoah_assert_correct(nullptr, orig); // Capture the original value that was in the field reference. + JavaThread* thread = JavaThread::current(); assert(ShenandoahThreadLocalData::satb_mark_queue(thread).is_active(), "Shouldn't be here otherwise"); SATBMarkQueue& queue = ShenandoahThreadLocalData::satb_mark_queue(thread); ShenandoahBarrierSet::satb_mark_queue_set().enqueue_known_active(queue, orig); JRT_END -void ShenandoahRuntime::write_barrier_pre(oopDesc* orig) { - write_ref_field_pre(orig, JavaThread::current()); -} - JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong(oopDesc* src, oop* load_addr)) return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr); JRT_END diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp index 0ed8959d95ec..f1919095d58f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp @@ -36,7 +36,6 @@ class ShenandoahRuntime : public AllStatic { static void arraycopy_barrier_oop(oop* src, oop* dst, size_t length); static void arraycopy_barrier_narrow_oop(narrowOop* src, narrowOop* dst, size_t length); - static void write_ref_field_pre(oopDesc* orig, JavaThread* thread); static void write_barrier_pre(oopDesc* orig); static oopDesc* load_reference_barrier_strong(oopDesc* src, oop* load_addr); diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 3d87e86e9bdb..300786daf054 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -48,14 +48,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" -const unsigned int initial_size = 431; - -static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); -} - -static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; - class ThreadIdExclusiveAccess : public StackObj { private: static Semaphore _mutex_semaphore; @@ -66,19 +58,13 @@ class ThreadIdExclusiveAccess : public StackObj { Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1); -static bool has_thread_exited(traceid tid) { - assert(tid != 0, "invariant"); - if (unloaded_thread_id_set == nullptr) { - return false; - } - ThreadIdExclusiveAccess lock; - return unloaded_thread_id_set->contains(tid); -} +static const unsigned initial_set_size = 512; +static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; static void add_to_unloaded_thread_set(traceid tid) { ThreadIdExclusiveAccess lock; if (unloaded_thread_id_set == nullptr) { - unloaded_thread_id_set = c_heap_allocate_set(); + unloaded_thread_id_set = new (mtTracing) JfrCHeapTraceIdSet(initial_set_size); } unloaded_thread_id_set->add(tid); } @@ -193,12 +179,6 @@ inline void BlobCache::on_unlink(BlobEntry* entry) const { assert(entry != nullptr, "invariant"); } -static JfrResourceAreaTraceIdSet* id_set = nullptr; - -static void prepare_for_resolution() { - id_set = new JfrResourceAreaTraceIdSet(initial_size); -} - static bool stack_trace_precondition(const ObjectSample* sample) { assert(sample != nullptr, "invariant"); return sample->has_stack_trace_id() && !sample->is_dead(); @@ -213,6 +193,8 @@ static void add_to_leakp_set(const ObjectSample* sample) { JfrTraceId::load_leakp(object->klass()); } +static JfrResourceAreaTraceIdSet* resolution_set = nullptr; + class StackTraceBlobInstaller { private: BlobCache _cache; @@ -220,8 +202,9 @@ class StackTraceBlobInstaller { const JfrStackTrace* resolve(const ObjectSample* sample) const; public: StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) { - prepare_for_resolution(); + resolution_set = new JfrResourceAreaTraceIdSet(initial_set_size); } + void sample_do(ObjectSample* sample) { if (stack_trace_precondition(sample)) { add_to_leakp_set(sample); @@ -314,8 +297,8 @@ static bool is_klass_unloaded(traceid klass_id) { static bool is_processed(traceid method_id) { assert(method_id != 0, "invariant"); - assert(id_set != nullptr, "invariant"); - return !id_set->add(method_id); + assert(resolution_set != nullptr, "invariant"); + return !resolution_set->add(method_id); } void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) { @@ -356,7 +339,7 @@ static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { assert(sample->has_thread(), "invariant"); - if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) { + if (sample->is_virtual_thread() || sample->thread_exited()) { write_blob(sample->thread(), writer); } } @@ -372,13 +355,13 @@ static inline bool should_write(const JfrStackTrace* stacktrace) { class LeakProfilerStackTraceWriter { private: JfrCheckpointWriter& _writer; - int _count; + unsigned _count; public: LeakProfilerStackTraceWriter(JfrCheckpointWriter& writer) : _writer(writer), _count(0) { assert(_stacktrace_id_set != nullptr, "invariant"); } - int count() const { return _count; } + unsigned count() const { return _count; } void operator()(const JfrStackTrace* stacktrace) { if (should_write(stacktrace)) { @@ -394,12 +377,10 @@ void ObjectSampleCheckpoint::write_stacktraces(Thread* thread) { JfrCheckpointWriter writer(thread); writer.write_type(TYPE_STACKTRACE); - const int64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet - + writer.write_count(_stacktrace_id_set->size()); LeakProfilerStackTraceWriter lpstw(writer); JfrStackTraceRepository::iterate_leakprofiler(lpstw); assert(lpstw.count() == _stacktrace_id_set->size(), "invariant"); - writer.write_count(lpstw.count(), count_offset); } static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { @@ -422,6 +403,16 @@ static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer) write_type_set_blob(sample, writer); } +static void check_if_thread_exited(const ObjectSample* sample) { + assert(sample != nullptr, "invariant"); + if (sample->thread_exited() || unloaded_thread_id_set == nullptr) { + return; + } + if (unloaded_thread_id_set->contains(sample->thread_id())) { + sample->set_thread_exited(); + } +} + class BlobWriter { private: const ObjectSampler* _sampler; @@ -431,23 +422,36 @@ class BlobWriter { BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : _sampler(sampler), _writer(writer), _last_sweep(last_sweep) {} void sample_do(ObjectSample* sample) { + check_if_thread_exited(sample); if (sample->is_alive_and_older_than(_last_sweep)) { write_blobs(sample, _writer); } } }; +static void delete_unloaded_thread_id_set() { + if (unloaded_thread_id_set != nullptr) { + delete unloaded_thread_id_set; + unloaded_thread_id_set = nullptr; + } +} + static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { // sample set is predicated on time of last sweep const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep(); JfrCheckpointWriter writer(thread, false); BlobWriter cbw(sampler, writer, last_sweep); + ThreadIdExclusiveAccess lock; iterate_samples(cbw, true); + delete_unloaded_thread_id_set(); } -static inline unsigned int set_size() { - const unsigned int queue_size = static_cast(JfrOptionSet::old_object_queue_size()); - return queue_size > initial_size ? queue_size : initial_size; +static inline unsigned stacktrace_id_set_size() { + unsigned queue_size = static_cast(JfrOptionSet::old_object_queue_size()); + if (!is_power_of_2(queue_size)) { + queue_size = next_power_of_2(queue_size); + } + return queue_size > initial_set_size ? queue_size : initial_set_size; } void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { @@ -456,7 +460,9 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge assert(thread != nullptr, "invariant"); { ResourceMark rm(thread); - _stacktrace_id_set = new JfrResourceAreaTraceIdSet(set_size()); + const unsigned stacktrace_set_size = stacktrace_id_set_size(); + assert(is_power_of_2(stacktrace_set_size), "invariant"); + _stacktrace_id_set = new JfrResourceAreaTraceIdSet(stacktrace_set_size); write_sample_blobs(sampler, emit_all, thread); if (_stacktrace_id_set->is_nonempty()) { write_stacktraces(thread); diff --git a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp index 214de827d03b..66ed9145c810 100644 --- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp +++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp @@ -59,6 +59,7 @@ class ObjectSample : public JfrCHeapObj { size_t _heap_used_at_last_gc; int _index; bool _virtual_thread; + mutable bool _thread_exited; void release_references() { _stacktrace.~JfrBlobHandle(); @@ -82,7 +83,8 @@ class ObjectSample : public JfrCHeapObj { _allocated(0), _heap_used_at_last_gc(0), _index(0), - _virtual_thread(false) {} + _virtual_thread(false), + _thread_exited(false) {} ObjectSample* next() const { return _next; @@ -225,6 +227,15 @@ class ObjectSample : public JfrCHeapObj { _virtual_thread = true; } + bool thread_exited() const { + return _thread_exited; + } + + void set_thread_exited() const { + assert(!_thread_exited, "invariant"); + _thread_exited = true; + } + const JfrBlobHandle& type_set() const { return _type_set; } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp index bf27fa590317..20e9c1e6798e 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp @@ -64,11 +64,11 @@ static void write_metadata_blob(JfrChunkWriter& chunkwriter, JavaThread* thread) chunkwriter.write_unbuffered(data_address, length); } -void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { +size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { assert(chunkwriter.is_valid(), "invariant"); check_internal_types(); if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) { - return; + return 0; } JavaThread* const jt = JavaThread::current(); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); @@ -87,6 +87,7 @@ void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset); chunkwriter.set_last_metadata_offset(metadata_offset); last_metadata_id = metadata_id; + return 1; } void JfrMetadataEvent::update(jbyteArray metadata) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp index abadbfb0b131..1b5bd45c9468 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -36,7 +36,7 @@ class JfrChunkWriter; // class JfrMetadataEvent : AllStatic { public: - static void write(JfrChunkWriter& writer); + static size_t write(JfrChunkWriter& writer); static void update(jbyteArray metadata); }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 69f002138eca..375ab4d04e9a 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -1048,19 +1048,15 @@ class MethodIteratorHost { private: MethodCallback _method_cb; KlassCallback _klass_cb; - KlassUsedPredicate _klass_used_predicate; - MethodUsedPredicate _method_used_predicate; MethodFlagPredicate _method_flag_predicate; public: MethodIteratorHost(JfrCheckpointWriter* writer) : _method_cb(writer, unloading(), false), _klass_cb(writer, unloading(), false), - _klass_used_predicate(current_epoch()), - _method_used_predicate(current_epoch()), _method_flag_predicate(current_epoch()) {} bool operator()(KlassPtr klass) { - if (_method_used_predicate(klass)) { + if (klass->is_instance_klass()) { const InstanceKlass* ik = InstanceKlass::cast(klass); while (ik != nullptr) { const int len = ik->methods()->length(); @@ -1075,7 +1071,7 @@ class MethodIteratorHost { ik = ik->previous_versions(); } } - return _klass_used_predicate(klass) ? _klass_cb(klass) : true; + return _klass_cb(klass); } int count() const { return _method_cb.count(); } @@ -1280,10 +1276,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer _class_unload = class_unload; _flushpoint = flushpoint; if (_artifacts == nullptr) { - _artifacts = new JfrArtifactSet(class_unload); + _artifacts = new JfrArtifactSet(class_unload, previous_epoch()); } else { - _artifacts->initialize(class_unload); + _artifacts->initialize(class_unload, previous_epoch()); } + assert(current_epoch() || _leakp_writer != nullptr, "invariant"); assert(_artifacts != nullptr, "invariant"); assert(!_artifacts->has_klass_entries(), "invariant"); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index d213ecd7d75f..c60556927ad0 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -29,18 +29,23 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" -JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(nullptr), - _klass_list(nullptr), - _total_count(0), - _class_unload(class_unload) { - initialize(class_unload); - assert(_klass_list != nullptr, "invariant"); +JfrArtifactSet::JfrArtifactSet(bool class_unload, bool previous_epoch) : _symbol_table(nullptr), + _klass_set(nullptr), + _klass_loader_set(nullptr), + _klass_loader_leakp_set(nullptr), + _total_count(0), + _class_unload(class_unload) { + initialize(class_unload, previous_epoch); + assert(!previous_epoch || _klass_loader_leakp_set != nullptr, "invariant"); + assert(_klass_loader_set != nullptr, "invariant"); + assert(_klass_set != nullptr, "invariant"); } -static const size_t initial_klass_list_size = 4096; -const int initial_klass_loader_set_size = 64; +static unsigned initial_klass_set_size = 4096; +static unsigned initial_klass_loader_set_size = 64; +static unsigned initial_klass_loader_leakp_set_size = 64; -void JfrArtifactSet::initialize(bool class_unload) { +void JfrArtifactSet::initialize(bool class_unload, bool previous_epoch) { _class_unload = class_unload; if (_symbol_table == nullptr) { _symbol_table = JfrSymbolTable::create(); @@ -50,9 +55,11 @@ void JfrArtifactSet::initialize(bool class_unload) { _symbol_table->set_class_unload(class_unload); _total_count = 0; // Resource allocations. Keep in this allocation order. - _klass_loader_leakp_set = new GrowableArray(initial_klass_loader_set_size); - _klass_loader_set = new GrowableArray(initial_klass_loader_set_size); - _klass_list = new GrowableArray(initial_klass_list_size); + if (previous_epoch) { + _klass_loader_leakp_set = new JfrKlassSet(initial_klass_loader_leakp_set_size); + } + _klass_loader_set = new JfrKlassSet(initial_klass_loader_set_size); + _klass_set = new JfrKlassSet(initial_klass_set_size); } void JfrArtifactSet::clear() { @@ -93,17 +100,12 @@ traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) } bool JfrArtifactSet::has_klass_entries() const { - return _klass_list->is_nonempty(); -} - -int JfrArtifactSet::entries() const { - return _klass_list->length(); + return _klass_set->is_nonempty(); } - -static inline bool not_in_set(GrowableArray* set, const Klass* k) { +static inline bool not_in_set(JfrArtifactSet::JfrKlassSet* set, const Klass* k) { assert(set != nullptr, "invariant"); assert(k != nullptr, "invariant"); - return !JfrMutablePredicate::test(set, k); + return set->add(k); } bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { @@ -116,16 +118,21 @@ bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { void JfrArtifactSet::register_klass(const Klass* k) { assert(k != nullptr, "invariant"); assert(IS_SERIALIZED(k), "invariant"); - assert(_klass_list != nullptr, "invariant"); - _klass_list->append(k); + assert(_klass_set != nullptr, "invariant"); + _klass_set->add(k); } size_t JfrArtifactSet::total_count() const { + assert(_klass_set != nullptr, "invariant"); + initial_klass_set_size = MAX2(initial_klass_set_size, _klass_set->table_size()); + assert(_klass_loader_set != nullptr, "invariant"); + initial_klass_loader_set_size = MAX2(initial_klass_loader_set_size, _klass_loader_set->table_size()); return _total_count; } void JfrArtifactSet::increment_checkpoint_id() { assert(_symbol_table != nullptr, "invariant"); _symbol_table->increment_checkpoint_id(); + assert(_klass_loader_leakp_set != nullptr, "invariant"); + initial_klass_loader_leakp_set_size = MAX2(initial_klass_loader_leakp_set_size, _klass_loader_leakp_set->table_size()); } - diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index 657aee9dc536..74200aef1f14 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -28,12 +28,10 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/support/jfrSymbolTable.hpp" #include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrSet.hpp" #include "oops/klass.hpp" #include "oops/method.hpp" -template -class GrowableArray; - // Composite callback/functor building block template class CompositeFunctor { @@ -135,27 +133,6 @@ class SymbolPredicate { } }; -class KlassUsedPredicate { - bool _current_epoch; - public: - KlassUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - -class MethodUsedPredicate { - bool _current_epoch; -public: - MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - if (!klass->is_instance_klass()) { - return false; - } - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - template class MethodFlagPredicate { bool _current_epoch; @@ -203,20 +180,46 @@ class LeakPredicate { * in the respective VM subsystems. */ class JfrArtifactSet : public JfrCHeapObj { + public: + class JfrArtifactSetConfig : public AllStatic { + public: + typedef const Klass* KEY_TYPE; + + constexpr static AnyObj::allocation_type alloc_type() { + return AnyObj::RESOURCE_AREA; + } + + constexpr static MemTag memory_tag() { + return mtInternal; + } + + // Knuth multiplicative hashing. + static uint32_t hash(const KEY_TYPE& k) { + const uint32_t v = static_cast(JfrTraceId::load_raw(k)); + return v * UINT32_C(2654435761); + } + + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { + return lhs == rhs; + } + }; + + typedef JfrSet JfrKlassSet; + private: JfrSymbolTable* _symbol_table; - GrowableArray* _klass_list; - GrowableArray* _klass_loader_set; - GrowableArray* _klass_loader_leakp_set; + JfrKlassSet* _klass_set; + JfrKlassSet* _klass_loader_set; + JfrKlassSet* _klass_loader_leakp_set; size_t _total_count; bool _class_unload; public: - JfrArtifactSet(bool class_unload); + JfrArtifactSet(bool class_unload, bool previous_epoch); ~JfrArtifactSet(); // caller needs ResourceMark - void initialize(bool class_unload); + void initialize(bool class_unload, bool previous_epoch); void clear(); traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); @@ -231,7 +234,6 @@ class JfrArtifactSet : public JfrCHeapObj { const JfrSymbolTable::StringEntry* map_string(uintptr_t hash) const; bool has_klass_entries() const; - int entries() const; size_t total_count() const; void register_klass(const Klass* k); bool should_do_cld_klass(const Klass* k, bool leakp); @@ -254,19 +256,17 @@ class JfrArtifactSet : public JfrCHeapObj { template void iterate_klasses(Functor& functor) const { - if (iterate(functor, _klass_list)) { + if (iterate(functor, _klass_set)) { iterate(functor, _klass_loader_set); } } private: template - bool iterate(Functor& functor, GrowableArray* list) const { - assert(list != nullptr, "invariant"); - for (int i = 0; i < list->length(); ++i) { - if (!functor(list->at(i))) { - return false; - } + bool iterate(Functor& functor, JfrKlassSet* set) const { + assert(set != nullptr, "invariant"); + if (set->is_nonempty()) { + set->iterate(functor); } return true; } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index a136f8f14760..ed273ab77598 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -368,13 +368,14 @@ static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter class MetadataEvent : public StackObj { private: JfrChunkWriter& _cw; + size_t _elements; public: - MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {} + MetadataEvent(JfrChunkWriter& cw) : _cw(cw), _elements(0) {} bool process() { - JfrMetadataEvent::write(_cw); + _elements = JfrMetadataEvent::write(_cw); return true; } - size_t elements() const { return 1; } + size_t elements() const { return _elements; } }; typedef WriteContent WriteMetadata; diff --git a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp index ce5de54ed164..d136eeab53ad 100644 --- a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp +++ b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp @@ -30,10 +30,10 @@ #include "runtime/mutexLocker.hpp" #include "utilities/macros.hpp" -static const int initial_size = 1009; +static const int initial_size = 1024; static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); + return new (mtTracing) JfrCHeapTraceIdSet(size); } // Track the set of unloaded klasses during a chunk / epoch. @@ -68,18 +68,9 @@ static JfrCHeapTraceIdSet* get_unload_set_previous_epoch() { return get_unload_set(JfrTraceIdEpoch::previous()); } -static bool is_nonempty_set(u1 epoch) { - if (epoch == 0) { - return _unload_set_epoch_0 != nullptr && _unload_set_epoch_0->is_nonempty(); - } - return _unload_set_epoch_1 != nullptr && _unload_set_epoch_1->is_nonempty(); -} - void JfrKlassUnloading::clear() { assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - if (is_nonempty_set(JfrTraceIdEpoch::previous())) { - get_unload_set_previous_epoch()->clear(); - } + get_unload_set_previous_epoch()->clear(); } static void add_to_unloaded_klass_set(traceid klass_id) { diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index b4dfc4f6240e..b6458d3f9a45 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -25,15 +25,13 @@ #ifndef SHARE_JFR_UTILITIES_JFRSET_HPP #define SHARE_JFR_UTILITIES_JFRSET_HPP -#include "jfr/utilities/jfrAllocation.hpp" +#include "memory/allocation.hpp" #include "jfr/utilities/jfrTypes.hpp" -#include "utilities/resizeableResourceHash.hpp" -template -class ConfigTraceID : public AllStatic { +template +class JfrSetConfig : public AllStatic { public: - typedef AllocPolicy STORAGE; - typedef traceid TYPE; + typedef K KEY_TYPE; constexpr static AnyObj::allocation_type alloc_type() { return AllocType; @@ -44,80 +42,171 @@ class ConfigTraceID : public AllStatic { } // Knuth multiplicative hashing. - static uint32_t hash(const TYPE& id) { - const uint32_t v = static_cast(id); - return v * UINT32_C(2654435761); + static uint32_t hash(const KEY_TYPE& key) { + const uint32_t k = static_cast(key); + return k * UINT32_C(2654435761); } - static bool cmp(const TYPE& lhs, const TYPE& rhs) { + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { return lhs == rhs; } }; -constexpr static unsigned int MAX_TABLE_SIZE = 0x3fffffff; - template -class JfrSet : public CONFIG::STORAGE { - public: - typedef typename CONFIG::TYPE TYPE; - typedef ResizeableResourceHashtable HashMap; - - constexpr static bool is_cheap() { - return CONFIG::alloc_type() == AnyObj::C_HEAP; +class JfrSetStorage : public AnyObj { + typedef typename CONFIG::KEY_TYPE K; + protected: + K* _table; + unsigned _table_size; + unsigned _elements; + + static K* alloc_table(unsigned table_size) { + K* table; + if (CONFIG::alloc_type() == C_HEAP) { + table = NEW_C_HEAP_ARRAY(K, table_size, CONFIG::memory_tag()); + } else { + table = NEW_RESOURCE_ARRAY(K, table_size); + } + memset(table, 0, table_size * sizeof(K)); + return table; } - JfrSet(unsigned int initial_size, unsigned int max_size = MAX_TABLE_SIZE) : - _map(is_cheap() ? new (CONFIG::memory_tag()) HashMap(initial_size, max_size) : new HashMap(initial_size, max_size)) {} + JfrSetStorage(unsigned table_size) : + _table(alloc_table(table_size)), + _table_size(table_size), + _elements(0) {} - ~JfrSet() { - if (is_cheap()) { - delete _map; + ~JfrSetStorage() { + if (CONFIG::alloc_type() == C_HEAP) { + FREE_C_HEAP_ARRAY(K, _table); } } - bool add(const TYPE& k) { - bool inserted; - _map->put_if_absent(k, &inserted); - return inserted; + public: + template + void iterate(Functor& functor) { + assert(is_nonempty(), "invariant"); + for (unsigned i = 0; i < _table_size; ++i) { + K k = _table[i]; + if (k != 0) { + functor(k); + } + } } - bool remove(const TYPE& k) { - return _map->remove(k); + unsigned table_size() const { + return _table_size; } - bool contains(const TYPE& k) const { - return _map->contains(k); + unsigned size() const { + return _elements; } - bool is_empty() const { - return _map->number_of_entries() == 0; + bool is_nonempty() const { + return _elements > 0; } - bool is_nonempty() const { - return !is_empty(); + void clear() { + memset(_table, 0, _table_size * sizeof(K)); } +}; - int size() const { - return _map->number_of_entries(); +template +class JfrSet : public JfrSetStorage { + typedef typename CONFIG::KEY_TYPE K; + static_assert(sizeof(K) > 1, "invalid size of CONFIG::KEY_TYPE"); + private: + static const constexpr unsigned max_initial_size = 1 << 30; + unsigned _table_mask; + unsigned _resize_threshold; // 0.5 load factor + + uint32_t slot_idx(const uint32_t hash) const { + return hash & _table_mask; } - void clear() { - if (is_nonempty()) { - _map->unlink(this); + void resize() { + assert(this->_elements == _resize_threshold, "invariant"); + K* const old_table = this->_table; + assert(old_table != nullptr, "invariant"); + const unsigned old_table_size = this->table_size(); + guarantee(old_table_size <= max_initial_size, "overflow"); + this->_table_size = old_table_size << 1; + this->_table = JfrSetStorage::alloc_table(this->_table_size); + _table_mask = this->_table_size - 1; + _resize_threshold = old_table_size; + for (unsigned i = 0; i < old_table_size; ++i) { + const K k = old_table[i]; + if (k != 0) { + uint32_t idx = slot_idx(CONFIG::hash(k)); + do { + K v = this->_table[idx]; + if (v == 0) { + this->_table[idx] = k; + break; + } + idx = slot_idx(idx + 1); + } while (true); + } } - assert(is_empty(), "invariant"); + if (CONFIG::alloc_type() == AnyObj::C_HEAP) { + FREE_C_HEAP_ARRAY(K, old_table); + } + assert(_table_mask + 1 == this->_table_size, "invariant"); + assert(_resize_threshold << 1 == this->_table_size, "invariant"); } - // Callback for node deletion, used by clear(). - bool do_entry(const TYPE& k, const TYPE& v) { - return true; + K* find_slot(K const& k) const { + uint32_t idx = slot_idx(CONFIG::hash(k)); + assert(idx < this->table_size(), "invariant"); + K* result = nullptr; + while (true) { + K v = this->_table[idx]; + if (v == 0) { + result = &this->_table[idx]; + break; + } + if (CONFIG::cmp(v, k)) { + result = reinterpret_cast(p2i(&this->_table[idx]) | 1); + break; + } + idx = slot_idx(idx + 1); + } + assert(result != nullptr, "invariant"); + return result; } - private: - HashMap* _map; + public: + JfrSet(unsigned size) : + JfrSetStorage(size), + _table_mask(size - 1), + _resize_threshold(size >> 1) { + assert(size >= 2, "invariant"); + assert(size % 2 == 0, "invariant"); + assert(size <= max_initial_size, "avoid overflow in resize"); + } + + bool contains(K const& k) const { + K* const slot = find_slot(k); + return p2i(slot) & 1; + } + + bool add(K const& k) { + K* const slot = find_slot(k); + if (p2i(slot) & 1) { + // Already exists. + return false; + } + assert(*slot == 0, "invariant"); + *slot = k; + if (++this->_elements == _resize_threshold) { + resize(); + } + assert(this->_elements < _resize_threshold, "invariant"); + return true; + } }; -typedef JfrSet > JfrCHeapTraceIdSet; -typedef JfrSet > JfrResourceAreaTraceIdSet; +typedef JfrSet > JfrCHeapTraceIdSet; +typedef JfrSet > JfrResourceAreaTraceIdSet; #endif // SHARE_JFR_UTILITIES_JFRSET_HPP diff --git a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp index 3dad778a73a4..cc96e4f2bbb6 100644 --- a/src/hotspot/share/oops/oop.inline.hpp +++ b/src/hotspot/share/oops/oop.inline.hpp @@ -120,7 +120,7 @@ Klass* oopDesc::klass_or_null() const { Klass* oopDesc::klass_or_null_acquire() const { switch (ObjLayout::klass_mode()) { case ObjLayout::Compact: - return mark_acquire().klass(); + return mark_acquire().klass_or_null(); case ObjLayout::Compressed: { narrowKlass narrow_klass = Atomic::load_acquire(&_metadata._compressed_klass); return CompressedKlassPointers::decode(narrow_klass); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 310d94d80aac..96ee0f9a225a 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -6886,7 +6886,8 @@ bool LibraryCallKit::inline_reference_get0() { DecoratorSet decorators = IN_HEAP | ON_WEAK_OOP_REF; Node* result = load_field_from_object(reference_obj, "referent", "Ljava/lang/Object;", - decorators, /*is_static*/ false, nullptr); + decorators, /*is_static*/ false, + env()->Reference_klass()); if (result == nullptr) return false; // Add memory barrier to prevent commoning reads from this field @@ -6909,7 +6910,8 @@ bool LibraryCallKit::inline_reference_refersTo0(bool is_phantom) { DecoratorSet decorators = IN_HEAP | AS_NO_KEEPALIVE; decorators |= (is_phantom ? ON_PHANTOM_OOP_REF : ON_WEAK_OOP_REF); Node* referent = load_field_from_object(reference_obj, "referent", "Ljava/lang/Object;", - decorators, /*is_static*/ false, nullptr); + decorators, /*is_static*/ false, + env()->Reference_klass()); if (referent == nullptr) return false; // Add memory barrier to prevent commoning reads from this field @@ -6988,8 +6990,6 @@ Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldNam assert(tinst != nullptr, "obj is null"); assert(tinst->is_loaded(), "obj is not loaded"); fromKls = tinst->instance_klass(); - } else { - assert(is_static, "only for static field access"); } ciField* field = fromKls->get_field_by_name(ciSymbol::make(fieldName), ciSymbol::make(fieldTypeString), diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index bc6a56dab055..4a7c613123ca 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -130,7 +130,7 @@ void UpcallLinker::on_exit(UpcallStub::FrameData* context) { JNIHandleBlock::release_block(context->new_handles, thread); } -void UpcallLinker::handle_uncaught_exception(oop exception) { +void UpcallLinker::handle_uncaught_exception(oopDesc* exception) { tty->print_cr("Uncaught exception:"); Handle exception_h(Thread::current(), exception); java_lang_Throwable::print_stack_trace(exception_h, tty); diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index f3275a22ef04..65699e0f1ffa 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -44,7 +44,7 @@ class UpcallLinker { bool needs_return_buffer, int ret_buf_size); // public for stubGenerator - static void handle_uncaught_exception(oop exception); + static void handle_uncaught_exception(oopDesc* exception); }; #endif // SHARE_VM_PRIMS_UPCALLLINKER_HPP diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index b9fa5374e7d9..3e273c871786 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -974,10 +974,7 @@ class os: AllStatic { // The thread_cpu_time() and current_thread_cpu_time() are only // supported if is_thread_cpu_time_supported() returns true. - // Thread CPU Time - return the fast estimate on a platform - // On Linux - fast clock_gettime where available - user+sys - // - otherwise: very slow /proc fs - user+sys - // On Windows - GetThreadTimes - user+sys + // Thread CPU Time - return the fast estimate on a platform - user+sys static jlong current_thread_cpu_time(); static jlong thread_cpu_time(Thread* t); diff --git a/src/hotspot/share/sanitizers/ub.hpp b/src/hotspot/share/sanitizers/ub.hpp index d5901f6821c3..8a6bfa50f51e 100644 --- a/src/hotspot/share/sanitizers/ub.hpp +++ b/src/hotspot/share/sanitizers/ub.hpp @@ -33,12 +33,9 @@ // Useful if the function or method is known to do something special or even 'dangerous', for // example causing desired signals/crashes. #ifdef UNDEFINED_BEHAVIOR_SANITIZER -#if defined(__clang__) +#if defined(__clang__) || defined(__GNUC__) #define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined","float-divide-by-zero"))) #endif -#if defined(__GNUC__) && !defined(__clang__) -#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined"))) -#endif #endif #ifndef ATTRIBUTE_NO_UBSAN diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java index 763013f280c2..9f860af101e0 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.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 @@ -101,7 +101,18 @@ public KEM.Encapsulated engineEncapsulate(int from, int to, return new KEM.Encapsulated( sub(dh, from, to), pkEm, null); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; + } catch (InvalidKeyException e) { + // Invalid peer public key + // Convert InvalidKeyException to an unchecked exception + throw new IllegalArgumentException("Invalid peer public key", + e); } catch (Exception e) { + // Unexpected internal failure throw new ProviderException("internal error", e); } } @@ -126,6 +137,11 @@ public SecretKey engineDecapsulate(byte[] encapsulation, int from, PublicKey pkE = params.DeserializePublicKey(encapsulation); SecretKey dh = params.DH(algorithm, skR, pkE); return sub(dh, from, to); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { @@ -248,7 +264,24 @@ private SecretKey DH(String alg, PrivateKey skE, PublicKey pkR) KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(alg); + SecretKey secret = ka.generateSecret(alg); + + // RFC 8446 section 7.4.2: checks for all-zero + // X25519/X448 shared secret. + if (this == X25519 || this == X448) { + byte[] s = secret.getEncoded(); + byte data = 0; + for (byte b : s) { + data |= b; + } + if (data == 0) { + // Trigger ILLEGAL_PARAMETER alert + throw new IllegalArgumentException( + "All-zero shared secret"); + } + } + + return secret; } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 39e82b504354..dea86351cc89 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ import sun.security.util.RawKeySpec; +import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; import javax.crypto.KeyAgreement; @@ -35,6 +36,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Provider; @@ -173,6 +175,9 @@ KEM.Encapsulated encapsulate(String algorithm, SecureRandom random) "encapsulation"); } + // All exceptions thrown during KEM encapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. try { KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : @@ -189,8 +194,18 @@ KEM.Encapsulated encapsulate(String algorithm, SecureRandom random) SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); return new KEM.Encapsulated(derived, enc.encapsulation(), null); - } catch (GeneralSecurityException gse) { - throw new SSLHandshakeException("Could not generate secret", gse); + } catch (IllegalArgumentException | InvalidKeyException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2), + // ML-KEM encapsulation key check failure (FIPS-203 section 7.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // Cryptographic failure, + // deriveHandshakeSecret failure. + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); } finally { KeyUtil.destroySecretKeys(sharedSecret); } @@ -208,13 +223,30 @@ private SecretKey t13DeriveKey(String type) // Using KEM: called by the client after receiving the KEM // ciphertext (keyshare) from the server in ServerHello. // The client decapsulates it using its private key. - KEM kem = (provider != null) - ? KEM.getInstance(algorithmName, provider) - : KEM.getInstance(algorithmName); - var decapsulator = kem.newDecapsulator(localPrivateKey); - sharedSecret = decapsulator.decapsulate( - keyshare, 0, decapsulator.secretSize(), - "TlsPremasterSecret"); + + // All exceptions thrown during KEM decapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. + try { + KEM kem = (provider != null) + ? KEM.getInstance(algorithmName, provider) + : KEM.getInstance(algorithmName); + var decapsulator = kem.newDecapsulator(localPrivateKey); + sharedSecret = decapsulator.decapsulate( + keyshare, 0, decapsulator.secretSize(), + "TlsPremasterSecret"); + } catch (IllegalArgumentException | InvalidKeyException | + DecapsulateException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // cryptographic failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); @@ -225,6 +257,7 @@ private SecretKey t13DeriveKey(String type) return deriveHandshakeSecret(type, sharedSecret); } catch (GeneralSecurityException gse) { + // deriveHandshakeSecret() failure throw new SSLHandshakeException("Could not generate secret", gse); } finally { KeyUtil.destroySecretKeys(sharedSecret); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderQueue.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderQueue.m index 8951ae8e110d..a143d6854607 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderQueue.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderQueue.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,19 @@ extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo); +/** + * Triggers the display link for the current destination surface. + */ +static void MTLSD_Flush() { + if (dstOps != NULL) { + MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps; + MTLLayer *layer = (MTLLayer*)dstMTLOps->layer; + if (layer != NULL) { + [layer startDisplayLink]; + } + } +} + void MTLRenderQueue_CheckPreviousOp(jint op) { if (mtlPreviousOp == op) { @@ -575,6 +588,7 @@ void MTLRenderQueue_CheckPreviousOp(jint op) { [cbwrapper release]; }]; [commandbuf commit]; + MTLSD_Flush(); } mtlc = [MTLContext setSurfacesEnv:env src:pSrc dst:pDst]; dstOps = (BMTLSDOps *)jlong_to_ptr(pDst); @@ -602,6 +616,7 @@ void MTLRenderQueue_CheckPreviousOp(jint op) { [cbwrapper release]; }]; [commandbuf commit]; + MTLSD_Flush(); } mtlc = newMtlc; dstOps = NULL; @@ -871,14 +886,7 @@ void MTLRenderQueue_CheckPreviousOp(jint op) { [cbwrapper release]; }]; [commandbuf commit]; - BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination(); - if (dstOps != NULL) { - MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps; - MTLLayer *layer = (MTLLayer*)dstMTLOps->layer; - if (layer != NULL) { - [layer startDisplayLink]; - } - } + MTLSD_Flush(); } RESET_PREVIOUS_OP(); } diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c b/src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c index 6328707a3e2a..42acf70a58f5 100644 --- a/src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -433,6 +433,7 @@ Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer jlong pDst = NEXT_LONG(b); if (oglc != NULL) { RESET_PREVIOUS_OP(); + OGLSD_Flush(env); } oglc = OGLContext_SetSurfaces(env, pSrc, pDst); dstOps = (OGLSDOps *)jlong_to_ptr(pDst); @@ -443,6 +444,7 @@ Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer jlong pConfigInfo = NEXT_LONG(b); if (oglc != NULL) { RESET_PREVIOUS_OP(); + OGLSD_Flush(env); } oglc = OGLSD_SetScratchSurface(env, pConfigInfo); dstOps = NULL; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index bc9efa30d9f6..bce66ae03f38 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4266,7 +4266,7 @@ public void visitRecordPattern(JCRecordPattern tree) { } List expectedRecordTypes; - if (site.tsym.kind == Kind.TYP && ((ClassSymbol) site.tsym).isRecord()) { + if (site.tsym instanceof ClassSymbol clazz && clazz.isRecord()) { ClassSymbol record = (ClassSymbol) site.tsym; expectedRecordTypes = record.getRecordComponents() .stream() diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestReferenceGet.java b/test/hotspot/jtreg/compiler/intrinsics/TestReferenceGet.java new file mode 100644 index 000000000000..6fca3e9df14f --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestReferenceGet.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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 8382815 + * @run main/othervm -XX:CompileCommand=dontinline,${test.main.class}::test_* ${test.main.class} + */ + +package compiler.intrinsics; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.PhantomReference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; + +public class TestReferenceGet { + private static final void fail(String msg) throws Exception { + throw new RuntimeException(msg); + } + + private static final void test0(Reference ref, + Object expectedValue, + Object unexpectedValue, + String kind) throws Exception { + if ((expectedValue != null) && ref.get() == null) { + fail(kind + " refers to null"); + } + if (ref.get() != expectedValue) { + fail(kind + " doesn't refer to expected value"); + } + if (ref.get() == unexpectedValue) { + fail(kind + " refers to unexpected value"); + } + } + + private static final void test_phantom0(PhantomReference ref, + String kind) throws Exception { + if (ref.get() != null) { + fail(kind + " does not refer to null"); + } + } + + // Entry points to the test, important to push down type information to + // individual test methods. + + private static final void test_phantom(PhantomReference ref) throws Exception { + test_phantom0(ref, "phantom"); + } + + private static final void test_phantom_shadow(ShadowPhantomReference ref) throws Exception { + test_phantom0(ref, "phantom shadow"); + } + + private static final void test_weak(WeakReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "weak"); + } + + private static final void test_weak_shadow(ShadowWeakReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "weak shadow"); + } + + private static final void test_soft(SoftReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "soft"); + } + + private static final void test_soft_shadow(ShadowSoftReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "soft shadow"); + } + + static Object unexpected = new Object(); + + static Object obj0 = new Object(); + static Object obj1 = new Object(); + static Object obj2 = new Object(); + static Object obj3 = new Object(); + static Object obj4 = new Object(); + static Object obj5 = new Object(); + + public static void main(String[] args) throws Exception { + var queue = new ReferenceQueue(); + + // It is important to do all test methods in the loop, so that we + // exercise all paths in intrinsics. + for (int i = 0; i < 100000; i++) { + System.out.println("Create"); + var pref = new PhantomReference(obj0, queue); + var wref = new WeakReference(obj1); + var sref = new SoftReference(obj2); + var psref = new ShadowPhantomReference<>(obj3, queue); + var wsref = new ShadowWeakReference<>(obj4); + var ssref = new ShadowSoftReference<>(obj5); + + System.out.println("After creation"); + test_phantom(pref); + test_weak(wref, obj1, unexpected); + test_soft(sref, obj2, unexpected); + test_phantom_shadow(psref); + test_weak_shadow(wsref, obj4, unexpected); + test_soft_shadow(ssref, obj5, unexpected); + + System.out.println("Cleaning references"); + pref.clear(); + wref.clear(); + sref.clear(); + psref.clear(); + wsref.clear(); + ssref.clear(); + + System.out.println("Testing after cleaning"); + test_phantom(pref); + test_weak(wref, null, unexpected); + test_soft(sref, null, unexpected); + test_phantom_shadow(psref); + test_weak_shadow(wsref, null, unexpected); + test_soft_shadow(ssref, null, unexpected); + } + } + + // References that have their own "shadow" referent. Check that intrinsics + // hit the right referent. + + static class ShadowSoftReference extends SoftReference { + T referent; + public ShadowSoftReference(T ref) { + super(ref); + referent = ref; + } + } + + static class ShadowWeakReference extends WeakReference { + T referent; + public ShadowWeakReference(T ref) { + super(ref); + referent = ref; + } + } + + static class ShadowPhantomReference extends PhantomReference { + T referent; + public ShadowPhantomReference(T ref, ReferenceQueue q) { + super(ref, q); + referent = ref; + } + } +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestReferenceRefersTo.java b/test/hotspot/jtreg/compiler/intrinsics/TestReferenceRefersTo.java index f9d36131a8b0..c66edb8a6ada 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestReferenceRefersTo.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestReferenceRefersTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,10 +23,13 @@ /* * @test - * @bug 8256377 + * @bug 8256377 8382815 * @summary Based on test/jdk/java/lang/ref/ReferenceRefersTo.java. + * @run main/othervm -XX:CompileCommand=dontinline,${test.main.class}::test_* ${test.main.class} */ +package compiler.intrinsics; + import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.PhantomReference; @@ -39,7 +42,7 @@ private static final void fail(String msg) throws Exception { } // Test java.lang.ref.Reference::refersTo0 intrinsic. - private static final void test(Reference ref, + private static final void test0(Reference ref, Object expectedValue, Object unexpectedValue, String kind) throws Exception { @@ -55,10 +58,10 @@ private static final void test(Reference ref, } // Test java.lang.ref.PhantomReference::refersTo0 intrinsic. - private static final void test_phantom(PhantomReference ref, + private static final void test_phantom0(PhantomReference ref, Object expectedValue, - Object unexpectedValue) throws Exception { - String kind = "phantom"; + Object unexpectedValue, + String kind) throws Exception { if ((expectedValue != null) && ref.refersTo(null)) { fail(kind + " refers to null"); } @@ -68,53 +71,120 @@ private static final void test_phantom(PhantomReference ref, if (ref.refersTo(unexpectedValue)) { fail(kind + " refers to unexpected value"); } + } + // Entry points to the test, important to push down type information to + // individual test methods. + + private static final void test_phantom(PhantomReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test_phantom0(ref, expectedValue, unexpectedValue, "phantom"); + } + + private static final void test_phantom_shadow(ShadowPhantomReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test_phantom0(ref, expectedValue, unexpectedValue, "phantom shadow"); } private static final void test_weak(WeakReference ref, Object expectedValue, Object unexpectedValue) throws Exception { - test(ref, expectedValue, unexpectedValue, "weak"); + test0(ref, expectedValue, unexpectedValue, "weak"); + } + + private static final void test_weak_shadow(ShadowWeakReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "weak shadow"); } private static final void test_soft(SoftReference ref, Object expectedValue, Object unexpectedValue) throws Exception { - test(ref, expectedValue, unexpectedValue, "soft"); + test0(ref, expectedValue, unexpectedValue, "soft"); + } + + private static final void test_soft_shadow(ShadowSoftReference ref, + Object expectedValue, + Object unexpectedValue) throws Exception { + test0(ref, expectedValue, unexpectedValue, "soft shadow"); } + static Object unexpected = new Object(); + + static Object obj0 = new Object(); + static Object obj1 = new Object(); + static Object obj2 = new Object(); + static Object obj3 = new Object(); + static Object obj4 = new Object(); + static Object obj5 = new Object(); + public static void main(String[] args) throws Exception { var queue = new ReferenceQueue(); - var obj0 = new Object(); - var obj1 = new Object(); - var obj2 = new Object(); - var obj3 = new Object(); + // It is important to do all test methods in the loop, so that we + // exercise all paths in intrinsics. + for (int i = 0; i < 100000; i++) { + System.out.println("Create"); + var pref = new PhantomReference(obj0, queue); + var wref = new WeakReference(obj1); + var sref = new SoftReference(obj2); + var psref = new ShadowPhantomReference<>(obj3, queue); + var wsref = new ShadowWeakReference<>(obj4); + var ssref = new ShadowSoftReference<>(obj5); + + System.out.println("After creation"); + test_phantom(pref, obj0, unexpected); + test_weak(wref, obj1, unexpected); + test_soft(sref, obj2, unexpected); + test_phantom_shadow(psref, obj3, unexpected); + test_weak_shadow(wsref, obj4, unexpected); + test_soft_shadow(ssref, obj5, unexpected); - var pref = new PhantomReference(obj0, queue); - var wref = new WeakReference(obj1); - var sref = new SoftReference(obj2); + System.out.println("Cleaning references"); + pref.clear(); + wref.clear(); + sref.clear(); + psref.clear(); + wsref.clear(); + ssref.clear(); - System.out.println("Warmup"); - for (int i = 0; i < 10000; i++) { - test_phantom(pref, obj0, obj3); - test_weak(wref, obj1, obj3); - test_soft(sref, obj2, obj3); + System.out.println("Testing after cleaning"); + test_phantom(pref, null, unexpected); + test_weak(wref, null, unexpected); + test_soft(sref, null, unexpected); + test_phantom_shadow(psref, null, unexpected); + test_weak_shadow(wsref, null, unexpected); + test_soft_shadow(ssref, null, unexpected); } + } - System.out.println("Testing starts"); - test_phantom(pref, obj0, obj3); - test_weak(wref, obj1, obj3); - test_soft(sref, obj2, obj3); + // References that have their own "shadow" referent. Check that intrinsics + // hit the right referent. - System.out.println("Cleaning references"); - pref.clear(); - wref.clear(); - sref.clear(); + static class ShadowSoftReference extends SoftReference { + T referent; + public ShadowSoftReference(T ref) { + super(ref); + referent = ref; + } + } - System.out.println("Testing after cleaning"); - test_phantom(pref, null, obj3); - test_weak(wref, null, obj3); - test_soft(sref, null, obj3); + static class ShadowWeakReference extends WeakReference { + T referent; + public ShadowWeakReference(T ref) { + super(ref); + referent = ref; + } + } + + static class ShadowPhantomReference extends PhantomReference { + T referent; + public ShadowPhantomReference(T ref, ReferenceQueue q) { + super(ref, q); + referent = ref; + } } } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 12869e136d3e..5a9129130504 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -248,7 +248,6 @@ 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/MultiWindowFillTest.java 8378506 macosx-all sun/java2d/OpenGL/OpaqueDest.java#id1 8367574 macosx-all sun/java2d/OpenGL/ScaleParamsOOB.java#id0 8377908 linux-all sun/java2d/SunGraphics2D/EmptyClipRenderingTest.java 8144029 macosx-all,linux-all diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 4a0f14baf0f1..e554400f13c7 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -109,8 +109,7 @@ tier4 = \ / \ -:tier1 \ -:tier2 \ - -:tier3 \ - :jdk_foreign_stress + -:tier3 ############################################################################### # @@ -393,11 +392,7 @@ jdk_svc = \ jdk_foreign = \ java/foreign \ jdk/internal/reflect/CallerSensitive/CheckCSMs.java \ - -java/foreign/TestMatrix.java \ - -java/foreign/TestUpcallStress.java - -jdk_foreign_stress = \ - java/foreign/TestUpcallStress.java + -java/foreign/TestMatrix.java jdk_vector = \ jdk/incubator/vector diff --git a/test/jdk/com/sun/net/httpserver/simpleserver/jwebserver/MaxRequestTimeTest.java b/test/jdk/com/sun/net/httpserver/simpleserver/jwebserver/MaxRequestTimeTest.java index c19ca632bf3b..836beeab8668 100644 --- a/test/jdk/com/sun/net/httpserver/simpleserver/jwebserver/MaxRequestTimeTest.java +++ b/test/jdk/com/sun/net/httpserver/simpleserver/jwebserver/MaxRequestTimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,27 +23,33 @@ /* * @test + * @key randomness * @bug 8278398 * @summary Tests the jwebserver's maximum request time * @modules jdk.httpserver * @library /test/lib + * @build jdk.test.lib.RandomFactory * @run junit/othervm MaxRequestTimeTest */ import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; + import jdk.test.lib.Platform; -import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.RandomFactory; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.FileUtils; @@ -61,16 +67,16 @@ * * The jwebserver has a maximum request time of 5 seconds, which is set with the * "sun.net.httpserver.maxReqTime" system property. If this threshold is - * reached, for example in the case of an HTTPS request where the server keeps - * waiting for a plaintext request, the server closes the connection. Subsequent + * reached, the server closes the connection. Subsequent * requests are expected to be handled as normal. * * The test checks in the following order that: * 1. an HTTP request is handled successfully, - * 2. an HTTPS request fails due to the server closing the connection + * 2. an incomplete HTTP request fails due to the server closing the connection * 3. another HTTP request is handled successfully. */ public class MaxRequestTimeTest { + private static final Random RND = RandomFactory.getRandom(); static final Path JAVA_HOME = Path.of(System.getProperty("java.home")); static final String LOCALE_OPT = "-J-Duser.language=en -J-Duser.country=US"; static final String JWEBSERVER = getJwebserver(JAVA_HOME); @@ -79,8 +85,6 @@ public class MaxRequestTimeTest { static final String LOOPBACK_ADDR = InetAddress.getLoopbackAddress().getHostAddress(); static final AtomicInteger PORT = new AtomicInteger(); - private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); - @BeforeAll public static void setup() throws IOException { if (Files.exists(TEST_DIR)) { @@ -94,10 +98,10 @@ public void testMaxRequestTime() throws Throwable { final var sb = new StringBuffer(); // stdout & stderr final var p = startProcess("jwebserver", sb); try { - sendHTTPSRequest(); // server expected to terminate connection - sendHTTPRequest(); // server expected to respond successfully - sendHTTPSRequest(); // server expected to terminate connection - sendHTTPRequest(); // server expected to respond successfully + sendIncompleteRequest(); // server expected to terminate connection + sendCompleteRequest(); // server expected to respond successfully + sendIncompleteRequest(); // server expected to terminate connection + sendCompleteRequest(); // server expected to respond successfully } finally { p.destroy(); int exitCode = p.waitFor(); @@ -105,6 +109,12 @@ public void testMaxRequestTime() throws Throwable { } } + static String requestText = """ + GET / HTTP/1.1\r + Host: localhost\r + \r + """; + static ByteBuffer requestBuffer = ByteBuffer.wrap(requestText.getBytes(StandardCharsets.UTF_8)); static String expectedBody = """ @@ -119,8 +129,8 @@ public void testMaxRequestTime() throws Throwable { """; - static void sendHTTPRequest() throws IOException, InterruptedException { - out.println("\n--- sendHTTPRequest"); + static void sendCompleteRequest() throws IOException, InterruptedException { + out.println("\n--- sendCompleteRequest"); var client = HttpClient.newBuilder() .proxy(NO_PROXY) .build(); @@ -129,18 +139,21 @@ static void sendHTTPRequest() throws IOException, InterruptedException { assertEquals(expectedBody, response.body()); } - static void sendHTTPSRequest() throws IOException, InterruptedException { - out.println("\n--- sendHTTPSRequest"); - var client = HttpClient.newBuilder() - .sslContext(sslContext) - .proxy(NO_PROXY) - .build(); - var request = HttpRequest.newBuilder(URI.create("https://localhost:" + PORT.get() + "/")).build(); - try { - client.send(request, HttpResponse.BodyHandlers.ofString()); - throw new RuntimeException("Expected SSLException not thrown"); - } catch (SSLException expected) { // server closes connection when max request time is reached - expected.printStackTrace(System.out); + static void sendIncompleteRequest() throws IOException { + out.println("\n--- sendIncompleteRequest"); + try (SocketChannel sc = SocketChannel.open( + new InetSocketAddress(LOOPBACK_ADDR, PORT.get()))) { + requestBuffer.clear(); + // only send a part of the HTTP request + int numBytes = RND.nextInt(1, requestBuffer.limit()); + System.out.println("Sending " + numBytes + " bytes"); + requestBuffer.limit(numBytes); + while (requestBuffer.hasRemaining()) { + sc.write(requestBuffer); + } + ByteBuffer responseBuffer = ByteBuffer.allocate(1); + int result = sc.read(responseBuffer); + assertEquals(-1, result); } } diff --git a/test/jdk/java/foreign/TestUpcallStress.java b/test/jdk/java/foreign/TestUpcallStress.java index d910723b5599..db5320eff372 100644 --- a/test/jdk/java/foreign/TestUpcallStress.java +++ b/test/jdk/java/foreign/TestUpcallStress.java @@ -24,14 +24,13 @@ /* * @test * @requires jdk.foreign.linker != "FALLBACK" - * @requires (os.arch == "aarch64" | os.arch=="riscv64") & os.name == "Linux" - * @requires os.maxMemory > 4G * @requires vm.compMode != "Xcomp" * @modules java.base/jdk.internal.foreign + * @library /test/lib * @build NativeTestHelper CallGeneratorHelper TestUpcallBase * @bug 8337753 * - * @run testng/native/othervm/timeout=3200 + * @run testng/native/othervm * -Xcheck:jni * -XX:+IgnoreUnrecognizedVMOptions * -XX:-VerifyDependencies @@ -44,7 +43,10 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.MemorySegment; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import jdk.test.lib.Utils; import java.lang.invoke.MethodHandle; import java.util.ArrayList; @@ -59,11 +61,27 @@ public class TestUpcallStress extends TestUpcallBase { System.loadLibrary("TestUpcall"); } + static final int THREAD_COUNT = 100; + + ExecutorService executor; + + @BeforeClass + public void setup() { + executor = Executors.newFixedThreadPool(THREAD_COUNT); + } + + @AfterClass + public void tearDown() throws InterruptedException { + executor.shutdown(); + // Let it run for a while, and then just terminate + executor.awaitTermination(Utils.adjustTimeout(30), TimeUnit.SECONDS); + } + + @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class) public void testUpcallsStress(int count, String fName, Ret ret, List paramTypes, - List fields) throws Throwable { - ExecutorService executor = Executors.newFixedThreadPool(16); - for (int threadIdx = 0; threadIdx < 16; threadIdx++) { + List fields) { + for (int threadIdx = 0; threadIdx < THREAD_COUNT; threadIdx++) { executor.submit(() -> { for (int iter = 0; iter < 10000; iter++) { List> returnChecks = new ArrayList<>(); @@ -91,8 +109,5 @@ public void testUpcallsStress(int count, String fName, Ret ret, List } }); } - // This shutdownNow is 'wrong', since it doesn't wait for tasks to terminate, - // but it seems to be the only way to reproduce the race of JDK-8337753 - executor.shutdownNow(); } } diff --git a/test/jdk/jdk/jfr/event/runtime/TestFlush.java b/test/jdk/jdk/jfr/event/runtime/TestFlush.java index 5c450eab05f8..fa570406f2a0 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestFlush.java +++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java @@ -143,8 +143,6 @@ private static void validateFlushEvent(RecordedEvent re) { printFlushEvent(re); Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type"); Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID"); - Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements"); - Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size"); } private static void acknowledgeFlushEvent() { diff --git a/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java b/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java index 59c58d944d79..302bb43e24a5 100644 --- a/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java +++ b/test/jdk/sun/java2d/OpenGL/MultiWindowFillTest.java @@ -34,7 +34,7 @@ /** * @test - * @bug 8378201 + * @bug 8378201 8378506 * @key headful * @summary Verifies that window content survives a GL context switch to another * window and back diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java index 4e1a7d7f6699..804c2a3f622e 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java @@ -1,5 +1,6 @@ /** * @test /nodynamiccopyright/ + * @bug 8375571 * @summary Verify error reports for erroneous deconstruction patterns are sensible * @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java */ @@ -39,6 +40,10 @@ case GenRecord(String s) -> {} boolean b = p instanceof P(int i) p; //introducing a variable for the record pattern } + void typeVarTest(T p) { + if (p instanceof T(int i) && i == 0); //T is a type variable + } + public record P(int i) { } diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out index f947142cd668..4d21d6069d83 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out @@ -1,23 +1,24 @@ -DeconstructionPatternErrors.java:35:37: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:37:28: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:39:42: compiler.err.expected: ';' -DeconstructionPatternErrors.java:39:43: compiler.err.not.stmt -DeconstructionPatternErrors.java:15:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) -DeconstructionPatternErrors.java:16:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList -DeconstructionPatternErrors.java:17:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) -DeconstructionPatternErrors.java:18:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) -DeconstructionPatternErrors.java:19:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) -DeconstructionPatternErrors.java:20:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:36:37: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:38:28: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:40:42: compiler.err.expected: ';' +DeconstructionPatternErrors.java:40:43: compiler.err.not.stmt +DeconstructionPatternErrors.java:16:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) +DeconstructionPatternErrors.java:17:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList +DeconstructionPatternErrors.java:18:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) +DeconstructionPatternErrors.java:19:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) +DeconstructionPatternErrors.java:20:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) DeconstructionPatternErrors.java:21:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable -DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none -DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int -DeconstructionPatternErrors.java:24:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) -DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable -DeconstructionPatternErrors.java:25:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:26:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:27:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) -DeconstructionPatternErrors.java:27:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:28:40: compiler.err.match.binding.exists -DeconstructionPatternErrors.java:29:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() -DeconstructionPatternErrors.java:29:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() -22 errors \ No newline at end of file +DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none +DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int +DeconstructionPatternErrors.java:25:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) +DeconstructionPatternErrors.java:25:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable +DeconstructionPatternErrors.java:26:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:27:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:28:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) +DeconstructionPatternErrors.java:28:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:29:40: compiler.err.match.binding.exists +DeconstructionPatternErrors.java:30:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() +DeconstructionPatternErrors.java:30:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() +DeconstructionPatternErrors.java:44:26: compiler.err.deconstruction.pattern.only.records: T +23 errors