diff --git a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp index 9f740098e19..07b28f2d23a 100644 --- a/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/downcallLinker_aarch64.cpp @@ -47,6 +47,7 @@ class DowncallStubGenerator : public StubCodeGenerator { bool _needs_return_buffer; int _captured_state_mask; + bool _needs_transition; int _frame_complete; int _frame_size_slots; @@ -60,7 +61,8 @@ class DowncallStubGenerator : public StubCodeGenerator { const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) + int captured_state_mask, + bool needs_transition) : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), @@ -70,6 +72,7 @@ class DowncallStubGenerator : public StubCodeGenerator { _output_registers(output_registers), _needs_return_buffer(needs_return_buffer), _captured_state_mask(captured_state_mask), + _needs_transition(needs_transition), _frame_complete(0), _frame_size_slots(0), _oop_maps(NULL) { @@ -99,12 +102,14 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int locs_size = 64; CodeBuffer code("nep_invoker_blob", native_invoker_code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, - needs_return_buffer, captured_state_mask); + needs_return_buffer, captured_state_mask, + needs_transition); g.generate(); code.log_section_sizes("nep_invoker_blob"); @@ -161,7 +166,7 @@ void DowncallStubGenerator::generate() { assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on AArch64"); allocated_frame_size += arg_shuffle.out_arg_bytes(); - bool should_save_return_value = !_needs_return_buffer; + bool should_save_return_value = !_needs_return_buffer && _needs_transition; RegSpiller out_reg_spiller(_output_registers); int spill_offset = -1; @@ -189,7 +194,7 @@ void DowncallStubGenerator::generate() { _frame_size_slots = align_up(framesize + (allocated_frame_size >> LogBytesPerInt), 4); assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned"); - _oop_maps = new OopMapSet(); + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; address start = __ pc(); __ enter(); @@ -199,15 +204,17 @@ void DowncallStubGenerator::generate() { _frame_complete = __ pc() - start; - address the_pc = __ pc(); - __ set_last_Java_frame(sp, rfp, the_pc, tmp1); - OopMap* map = new OopMap(_frame_size_slots, 0); - _oop_maps->add_gc_map(the_pc - start, map); + if (_needs_transition) { + address the_pc = __ pc(); + __ set_last_Java_frame(sp, rfp, the_pc, tmp1); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); - // State transition - __ mov(tmp1, _thread_in_native); - __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); - __ stlrw(tmp1, tmp2); + // State transition + __ mov(tmp1, _thread_in_native); + __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); + __ stlrw(tmp1, tmp2); + } __ block_comment("{ argument shuffle"); arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs); @@ -255,85 +262,88 @@ void DowncallStubGenerator::generate() { ////////////////////////////////////////////////////////////////////////////// - __ mov(tmp1, _thread_in_native_trans); - __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset())); - - // Force this write out before the read below - if (!UseSystemMemoryBarrier) { - __ membar(Assembler::LoadLoad | Assembler::LoadStore | - Assembler::StoreLoad | Assembler::StoreStore); - } - - __ verify_sve_vector_length(tmp1); - Label L_after_safepoint_poll; Label L_safepoint_poll_slow_path; + Label L_reguard; + Label L_after_reguard; + if (_needs_transition) { + __ mov(tmp1, _thread_in_native_trans); + __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset())); + + // Force this write out before the read below + if (!UseSystemMemoryBarrier) { + __ membar(Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore); + } - __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1); + __ verify_sve_vector_length(tmp1); - __ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset())); - __ cbnzw(tmp1, L_safepoint_poll_slow_path); + __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1); - __ bind(L_after_safepoint_poll); + __ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset())); + __ cbnzw(tmp1, L_safepoint_poll_slow_path); - // change thread state - __ mov(tmp1, _thread_in_Java); - __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); - __ stlrw(tmp1, tmp2); + __ bind(L_after_safepoint_poll); - __ block_comment("reguard stack check"); - Label L_reguard; - Label L_after_reguard; - __ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset())); - __ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled); - __ br(Assembler::EQ, L_reguard); - __ bind(L_after_reguard); + // change thread state + __ mov(tmp1, _thread_in_Java); + __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset())); + __ stlrw(tmp1, tmp2); + + __ block_comment("reguard stack check"); + __ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset())); + __ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled); + __ br(Assembler::EQ, L_reguard); + __ bind(L_after_reguard); - __ reset_last_Java_frame(true); + __ reset_last_Java_frame(true); + } __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(lr); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_safepoint_poll_slow_path"); - __ bind(L_safepoint_poll_slow_path); + if (_needs_transition) { + __ block_comment("{ L_safepoint_poll_slow_path"); + __ bind(L_safepoint_poll_slow_path); - if (should_save_return_value) { - // Need to save the native result registers around any runtime calls. - out_reg_spiller.generate_spill(_masm, spill_offset); - } + if (should_save_return_value) { + // Need to save the native result registers around any runtime calls. + out_reg_spiller.generate_spill(_masm, spill_offset); + } - __ mov(c_rarg0, rthread); - assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); - __ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - __ blr(tmp1); + __ mov(c_rarg0, rthread); + assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area"); + __ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ blr(tmp1); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } - __ b(L_after_safepoint_poll); - __ block_comment("} L_safepoint_poll_slow_path"); + __ b(L_after_safepoint_poll); + __ block_comment("} L_safepoint_poll_slow_path"); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_reguard"); - __ bind(L_reguard); + __ block_comment("{ L_reguard"); + __ bind(L_reguard); - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_offset); + } - __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); + __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } - __ b(L_after_reguard); + __ b(L_after_reguard); - __ block_comment("} L_reguard"); + __ block_comment("} L_reguard"); + } ////////////////////////////////////////////////////////////////////////////// diff --git a/src/hotspot/cpu/arm/downcallLinker_arm.cpp b/src/hotspot/cpu/arm/downcallLinker_arm.cpp index 37b6f43ac14..3174ca67682 100644 --- a/src/hotspot/cpu/arm/downcallLinker_arm.cpp +++ b/src/hotspot/cpu/arm/downcallLinker_arm.cpp @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index 13679cf6669..ea7465d3e76 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp index 06f8473403b..47ba3a59096 100644 --- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp @@ -100,7 +100,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int locs_size = 64; CodeBuffer code("nep_invoker_blob", native_invoker_code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index 37b6f43ac14..3174ca67682 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp index 3f1241970f2..c9df5869daf 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_32.cpp @@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp index 9637057b6ab..9a6893cad0e 100644 --- a/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/downcallLinker_x86_64.cpp @@ -46,6 +46,7 @@ class DowncallStubGenerator : public StubCodeGenerator { bool _needs_return_buffer; int _captured_state_mask; + bool _needs_transition; int _frame_complete; int _frame_size_slots; @@ -59,7 +60,8 @@ class DowncallStubGenerator : public StubCodeGenerator { const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) + int captured_state_mask, + bool needs_transition) : StubCodeGenerator(buffer, PrintMethodHandleStubs), _signature(signature), _num_args(num_args), @@ -69,6 +71,7 @@ class DowncallStubGenerator : public StubCodeGenerator { _output_registers(output_registers), _needs_return_buffer(needs_return_buffer), _captured_state_mask(captured_state_mask), + _needs_transition(needs_transition), _frame_complete(0), _frame_size_slots(0), _oop_maps(NULL) { @@ -98,12 +101,14 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { int locs_size = 64; CodeBuffer code("nep_invoker_blob", native_invoker_code_size, locs_size); DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, - needs_return_buffer, captured_state_mask); + needs_return_buffer, captured_state_mask, + needs_transition); g.generate(); code.log_section_sizes("nep_invoker_blob"); @@ -159,7 +164,7 @@ void DowncallStubGenerator::generate() { allocated_frame_size += arg_shuffle.out_arg_bytes(); // when we don't use a return buffer we need to spill the return value around our slow path calls - bool should_save_return_value = !_needs_return_buffer; + bool should_save_return_value = !_needs_return_buffer && _needs_transition; RegSpiller out_reg_spiller(_output_registers); int spill_rsp_offset = -1; @@ -188,7 +193,7 @@ void DowncallStubGenerator::generate() { _frame_size_slots += framesize_base + (allocated_frame_size >> LogBytesPerInt); assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned"); - _oop_maps = new OopMapSet(); + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; address start = __ pc(); __ enter(); @@ -198,16 +203,17 @@ void DowncallStubGenerator::generate() { _frame_complete = __ pc() - start; - address the_pc = __ pc(); + if (_needs_transition) { + __ block_comment("{ thread java2native"); + address the_pc = __ pc(); + __ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); - __ block_comment("{ thread java2native"); - __ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1); - OopMap* map = new OopMap(_frame_size_slots, 0); - _oop_maps->add_gc_map(the_pc - start, map); - - // State transition - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); - __ block_comment("} thread java2native"); + // State transition + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native); + __ block_comment("} thread java2native"); + } __ block_comment("{ argument shuffle"); arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs); @@ -261,93 +267,95 @@ void DowncallStubGenerator::generate() { ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ thread native2java"); - __ restore_cpu_control_state_after_jni(rscratch1); - - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); - - // Force this write out before the read below - if (!UseSystemMemoryBarrier) { - __ membar(Assembler::Membar_mask_bits( - Assembler::LoadLoad | Assembler::LoadStore | - Assembler::StoreLoad | Assembler::StoreStore)); - } - Label L_after_safepoint_poll; Label L_safepoint_poll_slow_path; + Label L_reguard; + Label L_after_reguard; + if (_needs_transition) { + __ block_comment("{ thread native2java"); + __ restore_cpu_control_state_after_jni(rscratch1); + + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans); - __ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); - __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); - __ jcc(Assembler::notEqual, L_safepoint_poll_slow_path); + // Force this write out before the read below + if (!UseSystemMemoryBarrier) { + __ membar(Assembler::Membar_mask_bits( + Assembler::LoadLoad | Assembler::LoadStore | + Assembler::StoreLoad | Assembler::StoreStore)); + } - __ bind(L_after_safepoint_poll); + __ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */); + __ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0); + __ jcc(Assembler::notEqual, L_safepoint_poll_slow_path); - // change thread state - __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); + __ bind(L_after_safepoint_poll); - __ block_comment("reguard stack check"); - Label L_reguard; - Label L_after_reguard; - __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled); - __ jcc(Assembler::equal, L_reguard); - __ bind(L_after_reguard); + // change thread state + __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); - __ reset_last_Java_frame(r15_thread, true); - __ block_comment("} thread native2java"); + __ block_comment("reguard stack check"); + __ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled); + __ jcc(Assembler::equal, L_reguard); + __ bind(L_after_reguard); + + __ reset_last_Java_frame(r15_thread, true); + __ block_comment("} thread native2java"); + } __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_safepoint_poll_slow_path"); - __ bind(L_safepoint_poll_slow_path); - __ vzeroupper(); + if (_needs_transition) { + __ block_comment("{ L_safepoint_poll_slow_path"); + __ bind(L_safepoint_poll_slow_path); + __ vzeroupper(); - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + } - __ mov(c_rarg0, r15_thread); - __ mov(r12, rsp); // remember sp - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows - __ andptr(rsp, -16); // align stack as required by ABI - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); - __ mov(rsp, r12); // restore sp - __ reinit_heapbase(); + __ mov(c_rarg0, r15_thread); + __ mov(r12, rsp); // remember sp + __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows + __ andptr(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans))); + __ mov(rsp, r12); // restore sp + __ reinit_heapbase(); - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); - } + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + } - __ jmp(L_after_safepoint_poll); - __ block_comment("} L_safepoint_poll_slow_path"); + __ jmp(L_after_safepoint_poll); + __ block_comment("} L_safepoint_poll_slow_path"); ////////////////////////////////////////////////////////////////////////////// - __ block_comment("{ L_reguard"); - __ bind(L_reguard); - __ vzeroupper(); - - if (should_save_return_value) { - out_reg_spiller.generate_spill(_masm, spill_rsp_offset); - } + __ block_comment("{ L_reguard"); + __ bind(L_reguard); + __ vzeroupper(); - __ mov(r12, rsp); // remember sp - __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows - __ andptr(rsp, -16); // align stack as required by ABI - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); - __ mov(rsp, r12); // restore sp - __ reinit_heapbase(); + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_rsp_offset); + } - if (should_save_return_value) { - out_reg_spiller.generate_fill(_masm, spill_rsp_offset); - } + __ mov(r12, rsp); // remember sp + __ subptr(rsp, frame::arg_reg_save_area_bytes); // windows + __ andptr(rsp, -16); // align stack as required by ABI + __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages))); + __ mov(rsp, r12); // restore sp + __ reinit_heapbase(); - __ jmp(L_after_reguard); + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_rsp_offset); + } - __ block_comment("} L_reguard"); + __ jmp(L_after_reguard); + __ block_comment("} L_reguard"); + } ////////////////////////////////////////////////////////////////////////////// __ flush(); diff --git a/src/hotspot/cpu/zero/downcallLinker_zero.cpp b/src/hotspot/cpu/zero/downcallLinker_zero.cpp index 3f1241970f2..c9df5869daf 100644 --- a/src/hotspot/cpu/zero/downcallLinker_zero.cpp +++ b/src/hotspot/cpu/zero/downcallLinker_zero.cpp @@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask) { + int captured_state_mask, + bool needs_transition) { Unimplemented(); return nullptr; } diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 86849c05158..b242f6cd74b 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -37,7 +37,8 @@ class DowncallLinker: AllStatic { const GrowableArray& input_registers, const GrowableArray& output_registers, bool needs_return_buffer, - int captured_state_mask); + int captured_state_mask, + bool needs_transition); static void capture_state(int32_t* value_ptr, int captured_state_mask); }; diff --git a/src/hotspot/share/prims/nativeEntryPoint.cpp b/src/hotspot/share/prims/nativeEntryPoint.cpp index 1a7ba6fe67b..acc853e0250 100644 --- a/src/hotspot/share/prims/nativeEntryPoint.cpp +++ b/src/hotspot/share/prims/nativeEntryPoint.cpp @@ -37,7 +37,8 @@ JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject method_type, jobject jabi, jobjectArray arg_moves, jobjectArray ret_moves, - jboolean needs_return_buffer, jint captured_state_mask)) + jboolean needs_return_buffer, jint captured_state_mask, + jboolean needs_transition)) ResourceMark rm; const ABIDescriptor abi = ForeignGlobals::parse_abi_descriptor(jabi); @@ -77,7 +78,8 @@ JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject metho return (jlong) DowncallLinker::make_downcall_stub(basic_type, pslots, ret_bt, abi, input_regs, output_regs, - needs_return_buffer, captured_state_mask)->code_begin(); + needs_return_buffer, captured_state_mask, + needs_transition)->code_begin(); JNI_END JNI_ENTRY(jboolean, NEP_freeDowncallStub(JNIEnv* env, jclass _unused, jlong invoker)) @@ -97,7 +99,7 @@ JNI_END #define VM_STORAGE_ARR "[Ljdk/internal/foreign/abi/VMStorage;" static JNINativeMethod NEP_methods[] = { - {CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZI)J", FN_PTR(NEP_makeDowncallStub)}, + {CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZIZ)J", FN_PTR(NEP_makeDowncallStub)}, {CC "freeDowncallStub0", CC "(J)Z", FN_PTR(NEP_freeDowncallStub)}, }; diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 7bb34888cdb..50470f057d4 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -75,6 +75,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { // modelled after JavaCallWrapper::JavaCallWrapper JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { JavaThread* thread = maybe_attach_and_get_thread(); + guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; assert(thread->can_call_java(), "must be able to call Java"); diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 38da21f12d4..3ab3b471990 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -315,6 +315,22 @@ static CaptureCallState captureCallState(String... capturedState) { return new LinkerOptions.CaptureCallStateImpl(set); } + /** + * {@return A linker option used to mark a foreign function as trivial} + *

+ * A trivial function is a function that has an extremely short running time + * in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub). + *

+ * Using this linker option is a hint which some implementations may use to apply + * optimizations that are only valid for trivial functions. + *

+ * Using this linker option when linking non trivial functions is likely to have adverse effects, + * such as loss of performance, or JVM crashes. + */ + static Option isTrivial() { + return LinkerOptions.IsTrivial.INSTANCE; + } + /** * A linker option for saving portions of the execution state immediately * after calling a foreign function associated with a downcall method handle, diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java index 458b174a4cb..26151713803 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CallingSequence.java @@ -191,6 +191,10 @@ public int capturedStateMask() { .reduce(0, (a, b) -> a | b); } + public boolean needsTransition() { + return !linkerOptions.isTrivial(); + } + public int numLeadingParams() { return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java index 8254e35682d..7da66e0a705 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java @@ -83,7 +83,8 @@ public MethodHandle getBoundMethodHandle() { toStorageArray(retMoves), leafType, callingSequence.needsReturnBuffer(), - callingSequence.capturedStateMask() + callingSequence.capturedStateMask(), + callingSequence.needsTransition() ); MethodHandle handle = JLIA.nativeMethodHandle(nep); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 7b194cf24fd..ecb5118f5bb 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -86,6 +86,11 @@ public boolean isVariadicFunction() { return fva != null; } + public boolean isTrivial() { + IsTrivial it = getOption(IsTrivial.class); + return it != null; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -99,8 +104,7 @@ public int hashCode() { } public sealed interface LinkerOptionImpl extends Linker.Option - permits FirstVariadicArg, - CaptureCallStateImpl { + permits CaptureCallStateImpl, FirstVariadicArg, IsTrivial { default void validateForDowncall(FunctionDescriptor descriptor) { throw new IllegalArgumentException("Not supported for downcall: " + this); } @@ -133,4 +137,13 @@ public StructLayout layout() { } } + public record IsTrivial() implements LinkerOptionImpl { + public static IsTrivial INSTANCE = new IsTrivial(); + + @Override + public void validateForDowncall(FunctionDescriptor descriptor) { + // always allowed + } + } + } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java index 9426c404c9f..5952c429214 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java @@ -47,7 +47,8 @@ public class NativeEntryPoint { private static final SoftReferenceCache NEP_CACHE = new SoftReferenceCache<>(); private record CacheKey(MethodType methodType, ABIDescriptor abi, List argMoves, List retMoves, - boolean needsReturnBuffer, int capturedStateMask) {} + boolean needsReturnBuffer, int capturedStateMask, + boolean needsTransition) {} private NativeEntryPoint(MethodType methodType, long downcallStubAddress) { this.methodType = methodType; @@ -58,15 +59,18 @@ public static NativeEntryPoint make(ABIDescriptor abi, VMStorage[] argMoves, VMStorage[] returnMoves, MethodType methodType, boolean needsReturnBuffer, - int capturedStateMask) { + int capturedStateMask, + boolean needsTransition) { if (returnMoves.length > 1 != needsReturnBuffer) { throw new AssertionError("Multiple register return, but needsReturnBuffer was false"); } checkType(methodType, needsReturnBuffer, capturedStateMask); - CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask); + CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), + needsReturnBuffer, capturedStateMask, needsTransition); return NEP_CACHE.get(key, k -> { - long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, capturedStateMask); + long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, + capturedStateMask, needsTransition); NativeEntryPoint nep = new NativeEntryPoint(methodType, downcallStub); CLEANER.register(nep, () -> freeDowncallStub(downcallStub)); return nep; @@ -87,7 +91,8 @@ private static void checkType(MethodType methodType, boolean needsReturnBuffer, private static native long makeDowncallStub(MethodType methodType, ABIDescriptor abi, VMStorage[] encArgMoves, VMStorage[] encRetMoves, boolean needsReturnBuffer, - int capturedStateMask); + int capturedStateMask, + boolean needsTransition); private static native boolean freeDowncallStub0(long downcallStub); private static void freeDowncallStub(long downcallStub) { diff --git a/test/jdk/java/foreign/UpcallTestHelper.java b/test/jdk/java/foreign/UpcallTestHelper.java index fa4ff862d51..5131605393a 100644 --- a/test/jdk/java/foreign/UpcallTestHelper.java +++ b/test/jdk/java/foreign/UpcallTestHelper.java @@ -37,13 +37,17 @@ public class UpcallTestHelper extends NativeTestHelper { public record Output(List stdout, List stderr) { - private static void assertContains(List lines, String shouldInclude) { + private static void assertContains(List lines, String shouldInclude, String name) { assertTrue(lines.stream().anyMatch(line -> line.contains(shouldInclude)), - "Did not find '" + shouldInclude + "' in stderr"); + "Did not find '" + shouldInclude + "' in " + name); } public void assertStdErrContains(String shouldInclude) { - assertContains(stderr, shouldInclude); + assertContains(stderr, shouldInclude, "stderr"); + } + + public void assertStdOutContains(String shouldInclude) { + assertContains(stdout, shouldInclude, "stdout"); } } diff --git a/test/jdk/java/foreign/trivial/TestTrivial.java b/test/jdk/java/foreign/trivial/TestTrivial.java new file mode 100644 index 00000000000..e6a2aba6450 --- /dev/null +++ b/test/jdk/java/foreign/trivial/TestTrivial.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * 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 + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestTrivial + */ + +import org.testng.annotations.Test; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.StructLayout; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; + +import static org.testng.Assert.assertEquals; + +public class TestTrivial extends NativeTestHelper { + + static { + System.loadLibrary("Trivial"); + } + + @Test + public void testEmpty() throws Throwable { + MethodHandle handle = downcallHandle("empty", FunctionDescriptor.ofVoid(), Linker.Option.isTrivial()); + handle.invokeExact(); + } + + @Test + public void testIdentity() throws Throwable { + MethodHandle handle = downcallHandle("identity", FunctionDescriptor.of(C_INT, C_INT), Linker.Option.isTrivial()); + int result = (int) handle.invokeExact(42); + assertEquals(result, 42); + } + + @Test + public void testWithReturnBuffer() throws Throwable { + StructLayout bigLayout = MemoryLayout.structLayout( + C_LONG_LONG.withName("x"), + C_LONG_LONG.withName("y")); + + MethodHandle handle = downcallHandle("with_return_buffer", FunctionDescriptor.of(bigLayout), Linker.Option.isTrivial()); + VarHandle vhX = bigLayout.varHandle(MemoryLayout.PathElement.groupElement("x")); + VarHandle vhY = bigLayout.varHandle(MemoryLayout.PathElement.groupElement("y")); + try (Arena arena = Arena.openConfined()) { + MemorySegment result = (MemorySegment) handle.invokeExact((SegmentAllocator) arena); + long x = (long) vhX.get(result); + assertEquals(x, 10); + long y = (long) vhY.get(result); + assertEquals(y, 11); + } + } + + @Test + public void testCaptureErrno() throws Throwable { + Linker.Option.CaptureCallState ccs = Linker.Option.captureCallState("errno"); + MethodHandle handle = downcallHandle("capture_errno", FunctionDescriptor.ofVoid(C_INT), Linker.Option.isTrivial(), ccs); + VarHandle errnoHandle = ccs.layout().varHandle(MemoryLayout.PathElement.groupElement("errno")); + try (Arena arena = Arena.openConfined()) { + MemorySegment captureSeg = arena.allocate(ccs.layout()); + handle.invokeExact(captureSeg, 42); + int capturedErrno = (int) errnoHandle.get(captureSeg); + assertEquals(capturedErrno, 42); + } + } + + +} diff --git a/test/jdk/java/foreign/trivial/TestTrivialUpcall.java b/test/jdk/java/foreign/trivial/TestTrivialUpcall.java new file mode 100644 index 00000000000..2c02ed2c7dd --- /dev/null +++ b/test/jdk/java/foreign/trivial/TestTrivialUpcall.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * 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 + * @enablePreview + * @library ../ /test/lib + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @requires vm.flavor != "zero" + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestTrivialUpcall + */ + +import org.testng.annotations.Test; + +import java.io.IOException; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +import static org.testng.Assert.fail; + +public class TestTrivialUpcall extends UpcallTestHelper { + + @Test + public void testUpcallFailure() throws IOException, InterruptedException { + // test to see if we catch a trivial downcall doing an upcall + runInNewProcess(Runner.class, true).assertStdOutContains("wrong thread state for upcall"); + } + + public static class Runner extends NativeTestHelper { + public static void main(String[] args) throws Throwable { + System.loadLibrary("Trivial"); + + MethodHandle mh = downcallHandle("do_upcall", FunctionDescriptor.ofVoid(C_POINTER), Linker.Option.isTrivial()); + MemorySegment stub = upcallStub(Runner.class, "target", FunctionDescriptor.ofVoid()); + mh.invokeExact(stub); + } + + public static void target() { + fail("Should not get here"); + } + } +} diff --git a/test/jdk/java/foreign/trivial/libTrivial.c b/test/jdk/java/foreign/trivial/libTrivial.c new file mode 100644 index 00000000000..bc3159f417f --- /dev/null +++ b/test/jdk/java/foreign/trivial/libTrivial.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +#ifdef _WIN64 +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +EXPORT void empty() {} + +EXPORT int identity(int value) { + return value; +} + +// 128 bit struct returned in buffer on SysV +struct Big { + long long x; + long long y; +}; + +EXPORT struct Big with_return_buffer() { + struct Big b; + b.x = 10; + b.y = 11; + return b; +} + +EXPORT void capture_errno(int value) { + errno = value; +} + +EXPORT void do_upcall(void(*f)(void)) { + f(); +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java index e542276ccf6..3e1614f32dc 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadConstant.java @@ -54,6 +54,11 @@ public void panama_blank() throws Throwable { func.invokeExact(); } + @Benchmark + public void panama_blank_trivial() throws Throwable { + func_trivial.invokeExact(); + } + @Benchmark public int jni_identity() throws Throwable { return identity(10); @@ -64,6 +69,11 @@ public int panama_identity() throws Throwable { return (int) identity.invokeExact(10); } + @Benchmark + public int panama_identity_trivial() throws Throwable { + return (int) identity_trivial.invokeExact(10); + } + @Benchmark public MemorySegment panama_identity_struct_confined() throws Throwable { return (MemorySegment) identity_struct.invokeExact(recycling_allocator, confinedPoint); diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java index bbbc0afbb0f..585d548ae3a 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadHelper.java @@ -41,10 +41,14 @@ public class CallOverheadHelper extends CLayouts { static final Linker abi = Linker.nativeLinker(); static final MethodHandle func; + static final MethodHandle func_trivial; static final MethodHandle func_v; + static final MethodHandle func_trivial_v; static MemorySegment func_addr; static final MethodHandle identity; + static final MethodHandle identity_trivial; static final MethodHandle identity_v; + static final MethodHandle identity_trivial_v; static MemorySegment identity_addr; static final MethodHandle identity_struct; static final MethodHandle identity_struct_v; @@ -98,13 +102,17 @@ public class CallOverheadHelper extends CLayouts { MethodType mt = MethodType.methodType(void.class); FunctionDescriptor fd = FunctionDescriptor.ofVoid(); func_v = abi.downcallHandle(fd); + func_trivial_v = abi.downcallHandle(fd, Linker.Option.isTrivial()); func = insertArguments(func_v, 0, func_addr); + func_trivial = insertArguments(func_trivial_v, 0, func_addr); } { identity_addr = loaderLibs.find("identity").orElseThrow(); FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT); identity_v = abi.downcallHandle(fd); + identity_trivial_v = abi.downcallHandle(fd, Linker.Option.isTrivial()); identity = insertArguments(identity_v, 0, identity_addr); + identity_trivial = insertArguments(identity_trivial_v, 0, identity_addr); } identity_struct_addr = loaderLibs.find("identity_struct").orElseThrow(); identity_struct_v = abi.downcallHandle( diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java index de3b275e034..6797eda9924 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/CallOverheadVirtual.java @@ -54,6 +54,11 @@ public void panama_blank() throws Throwable { func_v.invokeExact(func_addr); } + @Benchmark + public void panama_blank_trivial() throws Throwable { + func_trivial_v.invokeExact(func_addr); + } + @Benchmark public int jni_identity() throws Throwable { return identity(10); @@ -103,6 +108,11 @@ public int panama_identity() throws Throwable { return (int) identity_v.invokeExact(identity_addr, 10); } + @Benchmark + public int panama_identity_trivial() throws Throwable { + return (int) identity_trivial_v.invokeExact(identity_addr, 10); + } + @Benchmark public MemorySegment panama_identity_struct() throws Throwable { return (MemorySegment) identity_struct_v.invokeExact(identity_struct_addr, recycling_allocator, point);