From 7625b29920e95f9b754057fe0a2c4ab0afa5cb0c Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 16 Oct 2024 14:08:10 +0000 Subject: [PATCH] 8329597: C2: Intrinsify Reference.clear Reviewed-by: rcastanedalo, eosterlund, kvn --- .../gc/z/zBarrierSetAssembler_aarch64.cpp | 10 +- .../gc/z/zBarrierSetAssembler_aarch64.hpp | 4 +- src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad | 3 +- .../cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp | 2 + src/hotspot/cpu/ppc/gc/z/z_ppc.ad | 3 +- .../riscv/gc/z/zBarrierSetAssembler_riscv.cpp | 2 + src/hotspot/cpu/riscv/gc/z/z_riscv.ad | 3 +- .../cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp | 2 + src/hotspot/cpu/x86/gc/z/z_x86_64.ad | 3 +- src/hotspot/share/classfile/vmIntrinsics.hpp | 2 + src/hotspot/share/classfile/vmSymbols.hpp | 1 + src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp | 5 + .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 7 + src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp | 14 +- src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp | 6 +- src/hotspot/share/gc/z/zBarrierSetRuntime.cpp | 8 + src/hotspot/share/gc/z/zBarrierSetRuntime.hpp | 2 + src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 + src/hotspot/share/opto/c2compiler.cpp | 2 + src/hotspot/share/opto/library_call.cpp | 44 ++++++ src/hotspot/share/opto/library_call.hpp | 1 + .../java/lang/ref/PhantomReference.java | 13 ++ .../classes/java/lang/ref/Reference.java | 20 ++- .../share/native/libjava/PhantomReference.c | 6 + .../c2/irTests/gc/ReferenceClearTests.java | 138 ++++++++++++++++++ .../bench/java/lang/ref/ReferenceClear.java | 81 ++++++++++ 26 files changed, 362 insertions(+), 21 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 466e77a4460..3f1898b6742 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -1189,6 +1189,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ lea(rscratch1, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } @@ -1307,11 +1309,11 @@ Label* ZLoadBarrierStubC2Aarch64::entry() { return ZBarrierStubC2::entry(); } -ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) - : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic), _deferred_emit(false) {} +ZStoreBarrierStubC2Aarch64::ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) + : ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive), _deferred_emit(false) {} -ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { - ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); +ZStoreBarrierStubC2Aarch64* ZStoreBarrierStubC2Aarch64::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { + ZStoreBarrierStubC2Aarch64* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2Aarch64(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp index 2f716140ed1..ad3a171c103 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp @@ -280,10 +280,10 @@ class ZStoreBarrierStubC2Aarch64 : public ZStoreBarrierStubC2 { private: bool _deferred_emit; - ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2Aarch64(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2Aarch64* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 56d45384779..088f92a0157 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2Aarch64* const stub = ZStoreBarrierStubC2Aarch64::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp index 89ab1b1edee..8a65022126e 100644 --- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp @@ -943,6 +943,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr(), R3_ARG1); } else if (stub->is_atomic()) { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr(), R3_ARG1); + } else if (stub->is_nokeepalive()) { + __ call_VM_leaf(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } else { __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), R3_ARG1); } diff --git a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad index 017574d40ff..bb696a4738f 100644 --- a/src/hotspot/cpu/ppc/gc/z/z_ppc.ad +++ b/src/hotspot/cpu/ppc/gc/z/z_ppc.ad @@ -83,7 +83,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Register z_color(masm, rnew_zpointer, rnew_zaddress); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, Address(ref_base, disp), rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_base, disp, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp index cbb918ade00..4a82bd9c2d0 100644 --- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp @@ -761,6 +761,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ la(t0, RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ la(t0, RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad index 5b545fe8012..24669f45eb4 100644 --- a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad +++ b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad @@ -82,7 +82,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address z_color(masm, node, rnew_zpointer, rnew_zaddress, tmp); } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, tmp, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp index 65d7c1e3303..bc51a2b4468 100644 --- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp @@ -1260,6 +1260,8 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr())); } else if (stub->is_atomic()) { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr())); + } else if (stub->is_nokeepalive()) { + __ call(RuntimeAddress(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr())); } else { __ call(RuntimeAddress(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr())); } diff --git a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad index 455d622acdf..f55ad70e861 100644 --- a/src/hotspot/cpu/x86/gc/z/z_x86_64.ad +++ b/src/hotspot/cpu/x86/gc/z/z_x86_64.ad @@ -91,7 +91,8 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address } } else { bool is_native = (node->barrier_data() & ZBarrierNative) != 0; - ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic); + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); } diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index b6ce21797a6..9bb8b2179ae 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -469,6 +469,8 @@ class methodHandle; do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \ do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \ + do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ + do_intrinsic(_PhantomReference_clear0, java_lang_ref_PhantomReference, clear0_name, void_method_signature, F_RN) \ \ /* support for com.sun.crypto.provider.AESCrypt and some of its callers */ \ do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AESCrypt") \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index a65ab86fa0a..3de6d81f106 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -426,6 +426,7 @@ class SerializeClosure; template(cs_name, "cs") \ template(get_name, "get") \ template(refersTo0_name, "refersTo0") \ + template(clear0_name, "clear0") \ template(put_name, "put") \ template(type_name, "type") \ template(findNative_name, "findNative") \ diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 4ec7e10cd9a..edbf0e90239 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -327,6 +327,7 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co bool in_heap = (decorators & IN_HEAP) != 0; bool tightly_coupled_alloc = (decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0; bool need_store_barrier = !(tightly_coupled_alloc && use_ReduceInitialCardMarks()) && (in_heap || anonymous); + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; if (access.is_oop() && need_store_barrier) { access.set_barrier_data(get_store_barrier(access)); if (tightly_coupled_alloc) { @@ -336,6 +337,10 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); } } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); + } return BarrierSetC2::store_at_resolved(access, val); } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 7ac9dcc2e81..691c78cd024 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -480,10 +480,17 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& const TypePtr* adr_type = access.addr().type(); Node* adr = access.addr().node(); + bool no_keepalive = (decorators & AS_NO_KEEPALIVE) != 0; + if (!access.is_oop()) { return BarrierSetC2::store_at_resolved(access, val); } + if (no_keepalive) { + // No keep-alive means no need for the pre-barrier. + return BarrierSetC2::store_at_resolved(access, val); + } + if (access.is_parse_access()) { C2ParseAccess& parse_access = static_cast(access); GraphKit* kit = parse_access.kit(); diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index a46681d131c..12ee400eb2d 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -239,21 +239,23 @@ void ZLoadBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, static_cast(this)); } -ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) { +ZStoreBarrierStubC2* ZStoreBarrierStubC2::create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive) { AARCH64_ONLY(fatal("Should use ZStoreBarrierStubC2Aarch64::create")); - ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic); + ZStoreBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZStoreBarrierStubC2(node, ref_addr, new_zaddress, new_zpointer, is_native, is_atomic, is_nokeepalive); register_stub(stub); return stub; } -ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic) +ZStoreBarrierStubC2::ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, + bool is_native, bool is_atomic, bool is_nokeepalive) : ZBarrierStubC2(node), _ref_addr(ref_addr), _new_zaddress(new_zaddress), _new_zpointer(new_zpointer), _is_native(is_native), - _is_atomic(is_atomic) {} + _is_atomic(is_atomic), + _is_nokeepalive(is_nokeepalive) {} Address ZStoreBarrierStubC2::ref_addr() const { return _ref_addr; @@ -275,6 +277,10 @@ bool ZStoreBarrierStubC2::is_atomic() const { return _is_atomic; } +bool ZStoreBarrierStubC2::is_nokeepalive() const { + return _is_nokeepalive; +} + void ZStoreBarrierStubC2::emit_code(MacroAssembler& masm) { ZBarrierSet::assembler()->generate_c2_store_barrier_stub(&masm, static_cast(this)); } diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp index bf46780226e..58f75441b91 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.hpp @@ -79,18 +79,20 @@ private: const Register _new_zpointer; const bool _is_native; const bool _is_atomic; + const bool _is_nokeepalive; protected: - ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + ZStoreBarrierStubC2(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); public: - static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic); + static ZStoreBarrierStubC2* create(const MachNode* node, Address ref_addr, Register new_zaddress, Register new_zpointer, bool is_native, bool is_atomic, bool is_nokeepalive); Address ref_addr() const; Register new_zaddress() const; Register new_zpointer() const; bool is_native() const; bool is_atomic() const; + bool is_nokeepalive() const; virtual void emit_code(MacroAssembler& masm); }; diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp index b41fec3d0a5..c267d9bb24a 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.cpp @@ -59,6 +59,10 @@ JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing(oo ZBarrier::store_barrier_on_heap_oop_field((zpointer*)p, false /* heal */); JRT_END +JRT_LEAF(void, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing(oop* p)) + ZBarrier::no_keep_alive_store_barrier_on_heap_oop_field((zpointer*)p); +JRT_END + JRT_LEAF(void, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing(oop* p)) ZBarrier::store_barrier_on_native_oop_field((zpointer*)p, false /* heal */); JRT_END @@ -126,6 +130,10 @@ address ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr() { return reinterpret_cast
(store_barrier_on_oop_field_without_healing); } +address ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr() { + return reinterpret_cast
(no_keepalive_store_barrier_on_oop_field_without_healing); +} + address ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr() { return reinterpret_cast
(store_barrier_on_native_oop_field_without_healing); } diff --git a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp index 8a81f162bf1..8d59be02e68 100644 --- a/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp +++ b/src/hotspot/share/gc/z/zBarrierSetRuntime.hpp @@ -40,6 +40,7 @@ private: static oopDesc* no_keepalive_load_barrier_on_phantom_oop_field_preloaded(oopDesc* o, oop* p); static void store_barrier_on_oop_field_with_healing(oop* p); static void store_barrier_on_oop_field_without_healing(oop* p); + static void no_keepalive_store_barrier_on_oop_field_without_healing(oop* p); static void store_barrier_on_native_oop_field_without_healing(oop* p); static void load_barrier_on_oop_array(oop* p, size_t length); static void clone(oopDesc* src, oopDesc* dst, size_t size); @@ -54,6 +55,7 @@ public: static address no_keepalive_load_barrier_on_phantom_oop_field_preloaded_addr(); static address store_barrier_on_oop_field_with_healing_addr(); static address store_barrier_on_oop_field_without_healing_addr(); + static address no_keepalive_store_barrier_on_oop_field_without_healing_addr(); static address store_barrier_on_native_oop_field_without_healing_addr(); static address load_barrier_on_oop_array_addr(); static address clone_addr(); diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 05081197c4b..a26cd5efe23 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -847,6 +847,7 @@ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_store_good)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_weak_oop_field_preloaded)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_load_barrier_on_phantom_oop_field_preloaded)) \ + ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing)) \ ZGC_ONLY(DECLARE_FUNCTION_FROM_ADDR(declare_function_with_value, ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing)) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 151c320cadd..1a00f17f505 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -766,6 +766,8 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_Reference_get: case vmIntrinsics::_Reference_refersTo0: case vmIntrinsics::_PhantomReference_refersTo0: + case vmIntrinsics::_Reference_clear0: + case vmIntrinsics::_PhantomReference_clear0: case vmIntrinsics::_Class_cast: case vmIntrinsics::_aescrypt_encryptBlock: case vmIntrinsics::_aescrypt_decryptBlock: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 4ab4eea6f8f..49750cd2697 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -580,6 +580,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get: return inline_reference_get(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); + case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); + case vmIntrinsics::_PhantomReference_clear0: return inline_reference_clear0(true); case vmIntrinsics::_Class_cast: return inline_Class_cast(); @@ -6962,6 +6964,48 @@ bool LibraryCallKit::inline_reference_refersTo0(bool is_phantom) { return true; } +//----------------------------inline_reference_clear0---------------------------- +// void java.lang.ref.Reference.clear0(); +// void java.lang.ref.PhantomReference.clear0(); +bool LibraryCallKit::inline_reference_clear0(bool is_phantom) { + // This matches the implementation in JVM_ReferenceClear, see the comments there. + + // Get arguments + Node* reference_obj = null_check_receiver(); + if (stopped()) return true; + + // Common access parameters + DecoratorSet decorators = IN_HEAP | AS_NO_KEEPALIVE; + decorators |= (is_phantom ? ON_PHANTOM_OOP_REF : ON_WEAK_OOP_REF); + Node* referent_field_addr = basic_plus_adr(reference_obj, java_lang_ref_Reference::referent_offset()); + const TypePtr* referent_field_addr_type = _gvn.type(referent_field_addr)->isa_ptr(); + const Type* val_type = TypeOopPtr::make_from_klass(env()->Object_klass()); + + Node* referent = access_load_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + val_type, + T_OBJECT, + decorators); + + IdealKit ideal(this); +#define __ ideal. + __ if_then(referent, BoolTest::ne, null()); + sync_kit(ideal); + access_store_at(reference_obj, + referent_field_addr, + referent_field_addr_type, + null(), + val_type, + T_OBJECT, + decorators); + __ sync_kit(this); + __ end_if(); + final_sync(ideal); +#undef __ + + return true; +} Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators, bool is_static, diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 4a853045174..c9b2a02d3f1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -297,6 +297,7 @@ class LibraryCallKit : public GraphKit { bool inline_divmod_methods(vmIntrinsics::ID id); bool inline_reference_get(); bool inline_reference_refersTo0(bool is_phantom); + bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); diff --git a/src/java.base/share/classes/java/lang/ref/PhantomReference.java b/src/java.base/share/classes/java/lang/ref/PhantomReference.java index e4747483497..47a989cde46 100644 --- a/src/java.base/share/classes/java/lang/ref/PhantomReference.java +++ b/src/java.base/share/classes/java/lang/ref/PhantomReference.java @@ -77,6 +77,19 @@ public non-sealed class PhantomReference extends Reference { @IntrinsicCandidate private native boolean refersTo0(Object o); + /* Override the implementation of Reference.clear. + * Phantom references are weaker than finalization, so the referent + * access needs to be handled differently for garbage collectors that + * do reference processing concurrently. + */ + @Override + void clearImpl() { + clear0(); + } + + @IntrinsicCandidate + private native void clear0(); + /** * Creates a new phantom reference that refers to the given object and * is registered with the given queue. diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 747aa64902f..88cd2a3190b 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -403,13 +403,23 @@ public abstract sealed class Reference * necessary. */ public void clear() { + clearImpl(); + } + + /* Implementation of clear(). A simple assignment of the referent field + * won't do for some garbage collectors. There is the override for phantom + * references, which requires different semantics. This method is also + * used by enqueue(). + * + *

This method exists only to avoid making clear0() virtual. Making + * clear0() virtual has the undesirable effect of C2 often preferring + * to call the native implementation over the intrinsic. + */ + void clearImpl() { clear0(); } - /* Implementation of clear(), also used by enqueue(). A simple - * assignment of the referent field won't do for some garbage - * collectors. - */ + @IntrinsicCandidate private native void clear0(); /* -- Operations on inactive FinalReferences -- */ @@ -511,7 +521,7 @@ public abstract sealed class Reference * it was not registered with a queue when it was created */ public boolean enqueue() { - clear0(); // Intentionally clear0() rather than clear() + clearImpl(); // Intentionally clearImpl() to dispatch to overridden method, if needed return this.queue.enqueue(this); } diff --git a/src/java.base/share/native/libjava/PhantomReference.c b/src/java.base/share/native/libjava/PhantomReference.c index b6d6e7297f1..54d4b7681dd 100644 --- a/src/java.base/share/native/libjava/PhantomReference.c +++ b/src/java.base/share/native/libjava/PhantomReference.c @@ -31,3 +31,9 @@ Java_java_lang_ref_PhantomReference_refersTo0(JNIEnv *env, jobject ref, jobject { return JVM_PhantomReferenceRefersTo(env, ref, o); } + +JNIEXPORT void JNICALL +Java_java_lang_ref_PhantomReference_clear0(JNIEnv *env, jobject ref) +{ + JVM_ReferenceClear(env, ref); +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java new file mode 100644 index 00000000000..c56ffc88778 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceClearTests.java @@ -0,0 +1,138 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.c2.irTests.gc; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import jdk.test.whitebox.gc.GC; + +import java.lang.ref.*; +import java.util.*; + +/* + * @test + * @bug 8329597 + * @summary Test that Reference.clear intrinsics are properly handled + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @requires vm.compiler2.enabled + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.gc.ReferenceClearTests + + */ +public class ReferenceClearTests { + + private static String[] args(String... add) { + List args = new ArrayList<>(); + + // Use PerMethodTrapLimit=0 to compile all branches in the intrinsics. + args.add("-XX:PerMethodTrapLimit=0"); + + // Forcefully inline all methods to reach the intrinsic code. + args.add("-XX:CompileCommand=inline,compiler.c2.irTests.gc.ReferenceClearTests::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.Reference::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.PhantomReference::*"); + + // Mix in test config code. + args.addAll(Arrays.asList(add)); + + return args.toArray(new String[0]); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + + int idx = 0; + if (GC.isSelectedErgonomically() && GC.Serial.isSupported()) { + // Serial does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they are + // folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseSerialGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Parallel.isSupported()) { + // Parallel does not have SATB/keep-alive barriers at all. + // There are inter-generational barriers on stores, but they + // should be folded away for null stores like clear(). + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseParallelGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.G1.isSupported()) { + // G1 does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseG1GC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Shenandoah.isSupported()) { + // Shenandoah has SATB/keep-alive barriers, but they should not be + // present clear()-s. There are load-reference barriers, which would + // confuse the tests, so we enable only SATB barriers. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ShenandoahGCMode=passive", + "-XX:+ShenandoahSATBBarrier", + "-XX:+UseShenandoahGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Z.isSupported()) { + // Z does not have barriers in C2 IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseZGC" + ))); + } + framework.start(); + } + + static final Object REF = new Object(); + + static final SoftReference SR = new SoftReference<>(REF); + static final WeakReference WR = new WeakReference<>(REF); + static final PhantomReference PR = new PhantomReference<>(REF, null); + + // We assert there is only a single load and a single store of Reference.referent. + // This serves as signal that no GC barriers are emitted in IR. + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void soft() { + SR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void weak() { + WR.clear(); + } + + @Test + @IR(counts = { IRNode.STORE, "1", + IRNode.LOAD, "1" }) + public void phantom() { + PR.clear(); + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java new file mode 100644 index 00000000000..6a7b4d69970 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ref/ReferenceClear.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.ref; + +import java.lang.ref.*; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class ReferenceClear { + + final Reference soft = new SoftReference<>(new Object()); + final Reference weak = new WeakReference<>(new Object()); + final Reference phantom = new PhantomReference<>(new Object(), null); + + @Benchmark + public void soft() { + soft.clear(); + } + + @Benchmark + public void soft_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference soft = new SoftReference<>(ref); + soft.clear(); + } + + @Benchmark + public void weak() { + weak.clear(); + } + + @Benchmark + public void weak_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference weak = new WeakReference<>(ref); + weak.clear(); + } + + @Benchmark + public void phantom() { + phantom.clear(); + } + + @Benchmark + public void phantom_new(Blackhole bh) { + Object ref = new Object(); + bh.consume(ref); + Reference phantom = new PhantomReference<>(ref, null); + phantom.clear(); + } + +}