< prev index next >

src/hotspot/cpu/aarch64/universalNativeInvoker_aarch64.cpp

Print this page
@@ -23,105 +23,300 @@
   */
  
  #include "precompiled.hpp"
  #include "asm/macroAssembler.hpp"
  #include "code/codeBlob.hpp"
+ #include "code/codeCache.hpp"
+ #include "code/vmreg.inline.hpp"
+ #include "compiler/oopMap.hpp"
+ #include "logging/logStream.hpp"
  #include "memory/resourceArea.hpp"
  #include "prims/universalNativeInvoker.hpp"
+ #include "runtime/stubCodeGenerator.hpp"
  
  #define __ _masm->
  
- void ProgrammableInvoker::Generator::generate() {
-   __ enter();
+ class NativeInvokerGenerator : public StubCodeGenerator {
+   BasicType* _signature;
+   int _num_args;
+   BasicType _ret_bt;
+   const ABIDescriptor& _abi;
  
-   // Name registers used in the stub code. These are all caller-save so
-   // may be clobbered by the call to the native function. Avoid using
-   // rscratch1 here as it's r8 which is the indirect result register in
-   // the standard ABI.
-   Register Rctx = r10, Rstack_size = r11;
-   Register Rwords = r12, Rtmp = r13;
-   Register Rsrc_ptr = r14, Rdst_ptr = r15;
+   const GrowableArray<VMReg>& _input_registers;
+   const GrowableArray<VMReg>& _output_registers;
  
-   assert_different_registers(Rctx, Rstack_size, rscratch1, rscratch2);
+   bool _needs_return_buffer;
  
-   // TODO: if the callee is not using the standard C ABI then we need to
-   //       preserve more registers here.
+   int _frame_complete;
+   int _framesize;
+   OopMapSet* _oop_maps;
+ public:
+   NativeInvokerGenerator(CodeBuffer* buffer,
+                          BasicType* signature,
+                          int num_args,
+                          BasicType ret_bt,
+                          const ABIDescriptor& abi,
+                          const GrowableArray<VMReg>& input_registers,
+                          const GrowableArray<VMReg>& output_registers,
+                          bool needs_return_buffer)
+    : StubCodeGenerator(buffer, PrintMethodHandleStubs),
+      _signature(signature),
+      _num_args(num_args),
+      _ret_bt(ret_bt),
+      _abi(abi),
+      _input_registers(input_registers),
+      _output_registers(output_registers),
+      _needs_return_buffer(needs_return_buffer),
+      _frame_complete(0),
+      _framesize(0),
+      _oop_maps(NULL) {
+   }
  
-   __ block_comment("init_and_alloc_stack");
+   void generate();
  
-   __ mov(Rctx, c_rarg0);
-   __ str(Rctx, Address(__ pre(sp, -2 * wordSize)));
+   int frame_complete() const {
+     return _frame_complete;
+   }
  
-   assert(_abi->_stack_alignment_bytes % 16 == 0, "stack must be 16 byte aligned");
+   int framesize() const {
+     return (_framesize >> (LogBytesPerWord - LogBytesPerInt));
+   }
  
-   __ block_comment("allocate_stack");
-   __ ldr(Rstack_size, Address(Rctx, (int) _layout->stack_args_bytes));
-   __ add(rscratch2, Rstack_size, _abi->_stack_alignment_bytes - 1);
-   __ andr(rscratch2, rscratch2, -_abi->_stack_alignment_bytes);
-   __ sub(sp, sp, rscratch2);
+   OopMapSet* oop_maps() const {
+     return _oop_maps;
+   }
+ };
  
-   __ block_comment("load_arguments");
+ static const int native_invoker_code_size = 1024;
  
-   __ ldr(Rsrc_ptr, Address(Rctx, (int) _layout->stack_args));
-   __ lsr(Rwords, Rstack_size, LogBytesPerWord);
-   __ mov(Rdst_ptr, sp);
+ RuntimeStub* ProgrammableInvoker::make_native_invoker(BasicType* signature,
+                                                       int num_args,
+                                                       BasicType ret_bt,
+                                                       const ABIDescriptor& abi,
+                                                       const GrowableArray<VMReg>& input_registers,
+                                                       const GrowableArray<VMReg>& output_registers,
+                                                       bool needs_return_buffer) {
+   int locs_size  = 64;
+   CodeBuffer code("nep_invoker_blob", native_invoker_code_size, locs_size);
+   NativeInvokerGenerator g(&code, signature, num_args, ret_bt, abi, input_registers, output_registers, needs_return_buffer);
+   g.generate();
+   code.log_section_sizes("nep_invoker_blob");
  
-   Label Ldone, Lnext;
-   __ bind(Lnext);
-   __ cbz(Rwords, Ldone);
-   __ ldr(Rtmp, __ post(Rsrc_ptr, wordSize));
-   __ str(Rtmp, __ post(Rdst_ptr, wordSize));
-   __ sub(Rwords, Rwords, 1);
-   __ b(Lnext);
-   __ bind(Ldone);
+   RuntimeStub* stub =
+     RuntimeStub::new_runtime_stub("nep_invoker_blob",
+                                   &code,
+                                   g.frame_complete(),
+                                   g.framesize(),
+                                   g.oop_maps(), false);
  
-   for (int i = 0; i < _abi->_vector_argument_registers.length(); i++) {
-     ssize_t offs = _layout->arguments_vector + i * float_reg_size;
-     __ ldrq(_abi->_vector_argument_registers.at(i), Address(Rctx, offs));
+   if (TraceNativeInvokers) {
+     stub->print_on(tty);
    }
  
-   for (int i = 0; i < _abi->_integer_argument_registers.length(); i++) {
-     ssize_t offs = _layout->arguments_integer + i * sizeof(uintptr_t);
-     __ ldr(_abi->_integer_argument_registers.at(i), Address(Rctx, offs));
+   return stub;
+ }
+ 
+ void NativeInvokerGenerator::generate() {
+   enum layout {
+     rfp_off,
+     rfp_off2,
+     lr_off,
+     lr_off2,
+     framesize // inclusive of return address
+     // The following are also computed dynamically:
+     // spill area for return value
+     // out arg area (e.g. for stack args)
+   };
+ 
+   // we can't use rscratch1 because it is r8, and used by the ABI
+   Register tmp1 = r9;
+   Register tmp2 = r10;
+ 
+   Register shuffle_reg = r19;
+   JavaCallConv in_conv;
+   NativeCallConv out_conv(_input_registers);
+   ArgumentShuffle arg_shuffle(_signature, _num_args, _signature, _num_args, &in_conv, &out_conv, shuffle_reg->as_VMReg());
+ 
+ #ifdef ASSERT
+   LogTarget(Trace, panama) lt;
+   if (lt.is_enabled()) {
+     ResourceMark rm;
+     LogStream ls(lt);
+     arg_shuffle.print_on(&ls);
    }
+ #endif
  
-   assert(_abi->_shadow_space_bytes == 0, "shadow space not supported on AArch64");
+   int allocated_frame_size = 0;
+   if (_needs_return_buffer) {
+     allocated_frame_size += 8; // for address spill
+   }
+   allocated_frame_size += arg_shuffle.out_arg_stack_slots() <<LogBytesPerInt;
+   assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on AArch64");
  
-   // call target function
-   __ block_comment("call target function");
-   __ ldr(rscratch2, Address(Rctx, (int) _layout->arguments_next_pc));
-   __ blr(rscratch2);
+   int ret_buf_addr_sp_offset = -1;
+   if (_needs_return_buffer) {
+      // in sync with the above
+      ret_buf_addr_sp_offset = allocated_frame_size - 8;
+   }
  
-   __ ldr(Rctx, Address(rfp, -2 * wordSize));   // Might have clobbered Rctx
+   RegSpiller out_reg_spiller(_output_registers);
+   int spill_offset = -1;
  
-   __ block_comment("store_registers");
+   if (!_needs_return_buffer) {
+     spill_offset = 0;
+     // spill area can be shared with the above, so we take the max of the 2
+     allocated_frame_size = out_reg_spiller.spill_size_bytes() > allocated_frame_size
+       ? out_reg_spiller.spill_size_bytes()
+       : allocated_frame_size;
+   }
  
-   for (int i = 0; i < _abi->_integer_return_registers.length(); i++) {
-     ssize_t offs = _layout->returns_integer + i * sizeof(uintptr_t);
-     __ str(_abi->_integer_return_registers.at(i), Address(Rctx, offs));
+   _framesize = align_up(framesize
+     + (allocated_frame_size >> LogBytesPerInt), 4);
+   assert(is_even(_framesize/2), "sp not 16-byte aligned");
+ 
+   _oop_maps  = new OopMapSet();
+   address start = __ pc();
+ 
+   __ enter();
+ 
+   // lr and fp are already in place
+   __ sub(sp, rfp, ((unsigned)_framesize-4) << LogBytesPerInt); // prolog
+ 
+   _frame_complete = __ pc() - start;
+ 
+   address the_pc = __ pc();
+   __ set_last_Java_frame(sp, rfp, the_pc, tmp1);
+   OopMap* map = new OopMap(_framesize, 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);
+ 
+   __ block_comment("{ argument shuffle");
+   arg_shuffle.generate(_masm, shuffle_reg->as_VMReg(), 0, _abi._shadow_space_bytes);
+   if (_needs_return_buffer) {
+     assert(ret_buf_addr_sp_offset != -1, "no return buffer addr spill");
+     __ str(_abi._ret_buf_addr_reg, Address(sp, ret_buf_addr_sp_offset));
    }
+   __ block_comment("} argument shuffle");
+ 
+   __ blr(_abi._target_addr_reg);
+   // this call is assumed not to have killed rthread
  
-   for (int i = 0; i < _abi->_vector_return_registers.length(); i++) {
-     ssize_t offs = _layout->returns_vector + i * float_reg_size;
-     __ strq(_abi->_vector_return_registers.at(i), Address(Rctx, offs));
+   if (!_needs_return_buffer) {
+     // Unpack native results.
+     switch (_ret_bt) {
+       case T_BOOLEAN: __ c2bool(r0);                     break;
+       case T_CHAR   : __ ubfx(r0, r0, 0, 16);            break;
+       case T_BYTE   : __ sbfx(r0, r0, 0, 8);             break;
+       case T_SHORT  : __ sbfx(r0, r0, 0, 16);            break;
+       case T_INT    : __ sbfx(r0, r0, 0, 32);            break;
+       case T_DOUBLE :
+       case T_FLOAT  :
+         // Result is in v0 we'll save as needed
+         break;
+       case T_VOID: break;
+       case T_LONG: break;
+       default       : ShouldNotReachHere();
+     }
+   } else {
+     assert(ret_buf_addr_sp_offset != -1, "no return buffer addr spill");
+     __ ldr(tmp1, Address(sp, ret_buf_addr_sp_offset));
+     int offset = 0;
+     for (int i = 0; i < _output_registers.length(); i++) {
+       VMReg reg = _output_registers.at(i);
+       if (reg->is_Register()) {
+         __ str(reg->as_Register(), Address(tmp1, offset));
+         offset += 8;
+       } else if(reg->is_FloatRegister()) {
+         __ strd(reg->as_FloatRegister(), Address(tmp1, offset));
+         offset += 16;
+       } else {
+         ShouldNotReachHere();
+       }
+     }
    }
  
-   __ leave();
+   __ mov(tmp1, _thread_in_native_trans);
+   __ strw(tmp1, Address(rthread, JavaThread::thread_state_offset()));
+ 
+   // Force this write out before the read below
+   __ 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;
+ 
+   __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1);
+ 
+   __ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset()));
+   __ cbnzw(tmp1, L_safepoint_poll_slow_path);
+ 
+   __ bind(L_after_safepoint_poll);
+ 
+   // change thread state
+   __ mov(tmp1, _thread_in_Java);
+   __ lea(tmp2, Address(rthread, JavaThread::thread_state_offset()));
+   __ stlrw(tmp1, tmp2);
+ 
+   __ 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);
+ 
+   __ reset_last_Java_frame(true);
+ 
+   __ leave(); // required for proper stackwalking of RuntimeStub frame
    __ ret(lr);
  
-   __ flush();
- }
+   //////////////////////////////////////////////////////////////////////////////
+ 
+   __ block_comment("{ L_safepoint_poll_slow_path");
+   __ bind(L_safepoint_poll_slow_path);
+ 
+   if (!_needs_return_buffer) {
+     // Need to save the native result registers around any runtime calls.
+     out_reg_spiller.generate_spill(_masm, spill_offset);
+   }
  
- address ProgrammableInvoker::generate_adapter(jobject jabi, jobject jlayout) {
-   ResourceMark rm;
-   const ABIDescriptor abi = ForeignGlobals::parse_abi_descriptor(jabi);
-   const BufferLayout layout = ForeignGlobals::parse_buffer_layout(jlayout);
+   __ 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);
  
-   BufferBlob* _invoke_native_blob = BufferBlob::create("invoke_native_blob", native_invoker_size);
+   if (!_needs_return_buffer) {
+     out_reg_spiller.generate_fill(_masm, spill_offset);
+   }
+ 
+   __ b(L_after_safepoint_poll);
+   __ block_comment("} L_safepoint_poll_slow_path");
+ 
+   //////////////////////////////////////////////////////////////////////////////
+ 
+   __ block_comment("{ L_reguard");
+   __ bind(L_reguard);
+ 
+   if (!_needs_return_buffer) {
+     out_reg_spiller.generate_spill(_masm, spill_offset);
+   }
  
-   CodeBuffer code2(_invoke_native_blob);
-   ProgrammableInvoker::Generator g2(&code2, &abi, &layout);
-   g2.generate();
-   code2.log_section_sizes("InvokeNativeBlob");
+   __ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1);
  
-   return _invoke_native_blob->code_begin();
+   if (!_needs_return_buffer) {
+     out_reg_spiller.generate_fill(_masm, spill_offset);
+   }
+ 
+   __ b(L_after_reguard);
+ 
+   __ block_comment("} L_reguard");
+ 
+   //////////////////////////////////////////////////////////////////////////////
+ 
+   __ flush();
  }
< prev index next >