# HG changeset patch # User zgu # Date 1571192543 14400 # Node ID 55fe0d93bdd39463196651b79c69f2d333e35046 # Parent e0bb6b38a6f6d8cd44e21061ebbe83ebf94f1ced 8229919: Support JNI Critical functions in object pinning API on x86_32 platforms Reviewed-by: rkennke, adinn diff -r e0bb6b38a6f6 -r 55fe0d93bdd3 src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Tue Oct 15 21:33:54 2019 +0000 +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp Tue Oct 15 22:22:23 2019 -0400 @@ -1304,6 +1304,97 @@ } } +// Registers need to be saved for runtime call +static Register caller_saved_registers[] = { + rcx, rdx, rsi, rdi +}; + +// Save caller saved registers except r1 and r2 +static void save_registers_except(MacroAssembler* masm, Register r1, Register r2) { + int reg_len = (int)(sizeof(caller_saved_registers) / sizeof(Register)); + for (int index = 0; index < reg_len; index ++) { + Register this_reg = caller_saved_registers[index]; + if (this_reg != r1 && this_reg != r2) { + __ push(this_reg); + } + } +} + +// Restore caller saved registers except r1 and r2 +static void restore_registers_except(MacroAssembler* masm, Register r1, Register r2) { + int reg_len = (int)(sizeof(caller_saved_registers) / sizeof(Register)); + for (int index = reg_len - 1; index >= 0; index --) { + Register this_reg = caller_saved_registers[index]; + if (this_reg != r1 && this_reg != r2) { + __ pop(this_reg); + } + } +} + +// Pin object, return pinned object or null in rax +static void gen_pin_object(MacroAssembler* masm, + Register thread, VMRegPair reg) { + __ block_comment("gen_pin_object {"); + + Label is_null; + Register tmp_reg = rax; + VMRegPair tmp(tmp_reg->as_VMReg()); + if (reg.first()->is_stack()) { + // Load the arg up from the stack + simple_move32(masm, reg, tmp); + reg = tmp; + } else { + __ movl(tmp_reg, reg.first()->as_Register()); + } + __ testptr(reg.first()->as_Register(), reg.first()->as_Register()); + __ jccb(Assembler::equal, is_null); + + // Save registers that may be used by runtime call + Register arg = reg.first()->is_Register() ? reg.first()->as_Register() : noreg; + save_registers_except(masm, arg, thread); + + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::pin_object), + thread, reg.first()->as_Register()); + + // Restore saved registers + restore_registers_except(masm, arg, thread); + + __ bind(is_null); + __ block_comment("} gen_pin_object"); +} + +// Unpin object +static void gen_unpin_object(MacroAssembler* masm, + Register thread, VMRegPair reg) { + __ block_comment("gen_unpin_object {"); + Label is_null; + + // temp register + __ push(rax); + Register tmp_reg = rax; + VMRegPair tmp(tmp_reg->as_VMReg()); + + simple_move32(masm, reg, tmp); + + __ testptr(rax, rax); + __ jccb(Assembler::equal, is_null); + + // Save registers that may be used by runtime call + Register arg = reg.first()->is_Register() ? reg.first()->as_Register() : noreg; + save_registers_except(masm, arg, thread); + + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::unpin_object), + thread, rax); + + // Restore saved registers + restore_registers_except(masm, arg, thread); + __ bind(is_null); + __ pop(rax); + __ block_comment("} gen_unpin_object"); +} + // Check GCLocker::needs_gc and enter the runtime if it's true. This // keeps a new JNI critical region from starting until a GC has been // forced. Save down any oops in registers and describe them in an @@ -1837,7 +1928,7 @@ __ get_thread(thread); - if (is_critical_native) { + if (is_critical_native && !Universe::heap()->supports_object_pinning()) { check_needs_gc_for_critical_native(masm, thread, stack_slots, total_c_args, total_in_args, oop_handle_offset, oop_maps, in_regs, in_sig_bt); } @@ -1875,6 +1966,11 @@ // OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + // Inbound arguments that need to be pinned for critical natives + GrowableArray pinned_args(total_in_args); + // Current stack slot for storing register based array argument + int pinned_slot = oop_handle_offset; + // Mark location of rbp, // map->set_callee_saved(VMRegImpl::stack2reg( stack_slots - 2), stack_slots * 2, 0, rbp->as_VMReg()); @@ -1886,7 +1982,28 @@ switch (in_sig_bt[i]) { case T_ARRAY: if (is_critical_native) { - unpack_array_argument(masm, in_regs[i], in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); + VMRegPair in_arg = in_regs[i]; + if (Universe::heap()->supports_object_pinning()) { + // gen_pin_object handles save and restore + // of any clobbered registers + gen_pin_object(masm, thread, in_arg); + pinned_args.append(i); + + // rax has pinned array + VMRegPair result_reg(rax->as_VMReg()); + if (!in_arg.first()->is_stack()) { + assert(pinned_slot <= stack_slots, "overflow"); + simple_move32(masm, result_reg, VMRegImpl::stack2reg(pinned_slot)); + pinned_slot += VMRegImpl::slots_per_word; + } else { + // Write back pinned value, it will be used to unpin this argument + __ movptr(Address(rbp, reg2offset_in(in_arg.first())), result_reg.first()->as_Register()); + } + // We have the array in register, use it + in_arg = result_reg; + } + + unpack_array_argument(masm, in_arg, in_elem_bt[i], out_regs[c_arg + 1], out_regs[c_arg]); c_arg++; break; } @@ -2079,6 +2196,26 @@ default : ShouldNotReachHere(); } + // unpin pinned arguments + pinned_slot = oop_handle_offset; + if (pinned_args.length() > 0) { + // save return value that may be overwritten otherwise. + save_native_result(masm, ret_type, stack_slots); + for (int index = 0; index < pinned_args.length(); index ++) { + int i = pinned_args.at(index); + assert(pinned_slot <= stack_slots, "overflow"); + if (!in_regs[i].first()->is_stack()) { + int offset = pinned_slot * VMRegImpl::stack_slot_size; + __ movl(in_regs[i].first()->as_Register(), Address(rsp, offset)); + pinned_slot += VMRegImpl::slots_per_word; + } + // gen_pin_object handles save and restore + // of any other clobbered registers + gen_unpin_object(masm, thread, in_regs[i]); + } + restore_native_result(masm, ret_type, stack_slots); + } + // Switch thread to "native transition" state before reading the synchronization state. // This additional state is necessary because reading and testing the synchronization // state is not atomic w.r.t. GC, as this scenario demonstrates: diff -r e0bb6b38a6f6 -r 55fe0d93bdd3 test/hotspot/jtreg/gc/CriticalNativeArgs.java --- a/test/hotspot/jtreg/gc/CriticalNativeArgs.java Tue Oct 15 21:33:54 2019 +0000 +++ b/test/hotspot/jtreg/gc/CriticalNativeArgs.java Tue Oct 15 22:22:23 2019 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2018, 2019, Red Hat, Inc. and/or its affiliates. * * 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 @@ -28,7 +28,7 @@ * @key gc * @bug 8199868 * @library / - * @requires (os.arch =="x86_64" | os.arch == "amd64") & vm.gc.Epsilon & !vm.graal.enabled + * @requires (os.arch =="x86_64" | os.arch == "amd64" | os.arch=="x86" | os.arch=="i386") & vm.gc.Epsilon & !vm.graal.enabled * @summary test argument unpacking nmethod wrapper of critical native method * @run main/othervm/native -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xcomp -Xmx256M -XX:+CriticalJNINatives gc.CriticalNativeArgs */ @@ -38,7 +38,7 @@ * @key gc * @bug 8199868 * @library / - * @requires (os.arch =="x86_64" | os.arch == "amd64") & vm.gc.Shenandoah & !vm.graal.enabled + * @requires (os.arch =="x86_64" | os.arch == "amd64" | os.arch=="x86" | os.arch=="i386") & vm.gc.Shenandoah & !vm.graal.enabled * @summary test argument unpacking nmethod wrapper of critical native method * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive -XX:+ShenandoahDegeneratedGC -Xcomp -Xmx512M -XX:+CriticalJNINatives gc.CriticalNativeArgs * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive -XX:-ShenandoahDegeneratedGC -Xcomp -Xmx512M -XX:+CriticalJNINatives gc.CriticalNativeArgs diff -r e0bb6b38a6f6 -r 55fe0d93bdd3 test/hotspot/jtreg/gc/stress/CriticalNativeStress.java --- a/test/hotspot/jtreg/gc/stress/CriticalNativeStress.java Tue Oct 15 21:33:54 2019 +0000 +++ b/test/hotspot/jtreg/gc/stress/CriticalNativeStress.java Tue Oct 15 22:22:23 2019 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2018, 2019, Red Hat, Inc. and/or its affiliates. * * 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 @@ -31,7 +31,7 @@ * @key gc * @bug 8199868 * @library / - * @requires (os.arch =="x86_64" | os.arch == "amd64") & vm.gc.Epsilon & !vm.graal.enabled + * @requires (os.arch =="x86_64" | os.arch == "amd64" | os.arch=="x86" | os.arch=="i386") & vm.gc.Epsilon & !vm.graal.enabled * @summary test argument pinning by nmethod wrapper of critical native method * @run main/othervm/native -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xcomp -Xmx1G -XX:+CriticalJNINatives gc.stress.CriticalNativeStress */ @@ -41,7 +41,7 @@ * @key gc * @bug 8199868 * @library / - * @requires (os.arch =="x86_64" | os.arch == "amd64") & vm.gc.Shenandoah & !vm.graal.enabled + * @requires (os.arch =="x86_64" | os.arch == "amd64" | os.arch=="x86" | os.arch=="i386") & vm.gc.Shenandoah & !vm.graal.enabled * @summary test argument pinning by nmethod wrapper of critical native method * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive -XX:-ShenandoahDegeneratedGC -Xcomp -Xmx512M -XX:+CriticalJNINatives gc.stress.CriticalNativeStress * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive -XX:+ShenandoahDegeneratedGC -Xcomp -Xmx512M -XX:+CriticalJNINatives gc.stress.CriticalNativeStress