[inside hotspot] java方法调用的StubCode

时间:2019-01-27 22:56:27 来源:互联网 作者: 神秘的大神 字体:

[inside hotspot] java方法调用的StubCode

众所周知jvm有invokestatic,invokedynamic,invokestatic,invokespecial,invokevirtual几条方法调用指令,每个负责调用不同的方法,
而这些方法调用落实到hotspot上都位于hotspotsrcsharevm untimejavaCalls.hppJavaCalls :

1. JavaCalls

class JavaCalls: AllStatic {   static void call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS);  public:   // call_special   // ------------   // The receiver must be first oop in argument list   static void call_special(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);   static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS); // No args   static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);   static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);    // virtual call   // ------------   // The receiver must be first oop in argument list   static void call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);   static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, TRAPS); // No args   static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);   static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);    // Static call   // -----------   static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);   static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);   static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);   static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);   static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, Handle arg3, TRAPS);    // Low-level interface   static void call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS); };

上面的方法是自解释的,对应各自的invoke*指令,这些call_static,call_virtual内部调用了call()函数:

void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {   assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls");   os::os_exception_wrapper(call_helper, result, method, args, THREAD); }

call()只是简单检查了一下线程信息,以及根据平台比如windows会使用结构化异常(SEH)包裹call_helper,最终执行方法调用的还是call_helper

void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {   ...     // 如果当前方法为空,则直接返回   if (method->is_empty_method()) {     assert(result->get_type() == T_VOID, "an empty method must return a void value");     return;   }    ...    //根据情况决定是否编译该方法,JIT和-Xcomp都有可能触发它   CompilationPolicy::compile_if_required(method, CHECK);    // 解释器入口点   address entry_point = method->from_interpreted_entry();   if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) {     entry_point = method->interpreter_entry();   }    // 确定返回值类型   BasicType result_type = runtime_type_from(result);   bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY);    // 返回值地址   intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());    // 确定receiver,如果是static函数就没有receiver   Handle receiver = (!method->is_static()) ? args->receiver() : Handle();    if (!thread->stack_guards_enabled()) {     thread->reguard_stack();   }    // 确认当前sp是否到达ShadowPages,即是否会触发栈溢出错误   address sp = os::current_stack_pointer();   if (!os::stack_shadow_pages_available(THREAD, method, sp)) {     // Throw stack overflow exception with preinitialized exception.     Exceptions::throw_stack_overflow_exception(THREAD, __FILE__, __LINE__, method);     return;   } else {     // Touch pages checked if the OS needs them to be touched to be mapped.     os::map_stack_shadow_pages(sp);   }     // 执行调用   { JavaCallWrapper link(method, receiver, result, CHECK);     { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner        StubRoutines::call_stub()(         (address)&link,         // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)         result_val_address,          // see NOTE above (compiler problem)         result_type,         method(),         entry_point,         args->parameters(),         args->size_of_parameters(),         CHECK       );        result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)       // Preserve oop return value across possible gc points       if (oop_result_flag) {         thread->set_vm_result((oop) result->get_jobject());       }     }   }      // 设置返回值   if (oop_result_flag) {     result->set_jobject((jobject)thread->vm_result());     thread->set_vm_result(NULL);   } }

call_helper又可以分为两步,第一步判断一下方法是否为空,是否可以JIT编译,是否还有栈空间可以等,第二步StubRoutines::call_stub()实际调用os+cpu限定的方法。
这个StubRoutines::call_stub()返回的是一个函数指针,指向的是平台特定的方法,所以这段代码:

      StubRoutines::call_stub()(         (address)&link,         // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)         result_val_address,          // see NOTE above (compiler problem)         result_type,         method(),         entry_point,         args->parameters(),         args->size_of_parameters(),         CHECK       );

call_stub()返回一个函数指针,指向依赖于操作系统和cpu架构的特定的方法,原因很简单,要执行native代码,得看看是什么cpu架构以便确定寄存器,看看什么os以便确定ABI。
然后传递8个参数到这个方法里面并执行这个方法。那么这个方法是什么呢?进入stubRoutines.cpp便知是StubRoutines::_call_stub_entry

2. windows+x86_64的stubGenerator

以x64为例,hotspotsrccpux86vmstubGenerator_x86_64.cppgenerate_call_stub()会负责初始化StubRoutines::_call_stub_entry函数,使用参数命令
-XX:+UnlockDiagnosticVMOptions -XX:+PrintStubCode可以输出generate_call_stub方法生成的汇编,对照着看非常舒服:

 address generate_call_stub(address& return_address) {     assert((int)frame::entry_frame_after_call_words == -(int)rsp_after_call_off + 1 &&            (int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off,            "adjust this code");     StubCodeMark mark(this, "StubRoutines", "call_stub");     address start = __ pc();      // same as in generate_catch_exception()!     const Address rsp_after_call(rbp, rsp_after_call_off * wordSize);      const Address call_wrapper  (rbp, call_wrapper_off   * wordSize);     const Address result        (rbp, result_off         * wordSize);     const Address result_type   (rbp, result_type_off    * wordSize);     const Address method        (rbp, method_off         * wordSize);     const Address entry_point   (rbp, entry_point_off    * wordSize);     const Address parameters    (rbp, parameters_off     * wordSize);     const Address parameter_size(rbp, parameter_size_off * wordSize);      // same as in generate_catch_exception()!     const Address thread        (rbp, thread_off         * wordSize);      const Address r15_save(rbp, r15_off * wordSize);     const Address r14_save(rbp, r14_off * wordSize);     const Address r13_save(rbp, r13_off * wordSize);     const Address r12_save(rbp, r12_off * wordSize);     const Address rbx_save(rbp, rbx_off * wordSize);      // stub code     __ enter();     __ subptr(rsp, -rsp_after_call_off * wordSize);

StubRoutines::call_stub [0x0000026b0a5d09d7, 0x0000026b0a5d0b44[ (365 bytes)   0x0000026b0a5d09d7: push   %rbp   0x0000026b0a5d09d8: mov    %rsp,%rbp   0x0000026b0a5d09db: sub    $0x1d8,%rsp

    // save register parameters #ifndef _WIN64     __ movptr(parameters,   c_rarg5); // parameters     __ movptr(entry_point,  c_rarg4); // entry_point #endif      __ movptr(method,       c_rarg3); // method     __ movl(result_type,  c_rarg2);   // result type     __ movptr(result,       c_rarg1); // result     __ movptr(call_wrapper, c_rarg0); // call wrapper

  // r9方法,r8d返回值类型,rdx,返回值,rcx即JavaCallsWrapper   0x0000026b0a5d09e2: mov    %r9,0x28(%rbp)   0x0000026b0a5d09e6: mov    %r8d,0x20(%rbp)   0x0000026b0a5d09ea: mov    %rdx,0x18(%rbp)   0x0000026b0a5d09ee: mov    %rcx,0x10(%rbp)

    // save regs belonging to calling function     __ movptr(rbx_save, rbx);     __ movptr(r12_save, r12);     __ movptr(r13_save, r13);     __ movptr(r14_save, r14);     __ movptr(r15_save, r15);     if (UseAVX > 2) {       __ movl(rbx, 0xffff);       __ kmovwl(k1, rbx);     } #ifdef _WIN64     int last_reg = 15;     if (UseAVX > 2) {       last_reg = 31;     }     if (VM_Version::supports_evex()) {       for (int i = xmm_save_first; i <= last_reg; i++) {         __ vextractf32x4(xmm_save(i), as_XMMRegister(i), 0);       }     } else {       for (int i = xmm_save_first; i <= last_reg; i++) {         __ movdqu(xmm_save(i), as_XMMRegister(i));       }     }

   // caller-save 寄存器   0x0000026b0a5d09f2: mov    %rbx,-0x8(%rbp)   0x0000026b0a5d09f6: mov    %r12,-0x20(%rbp)   0x0000026b0a5d09fa: mov    %r13,-0x28(%rbp)   0x0000026b0a5d09fe: mov    %r14,-0x30(%rbp)   0x0000026b0a5d0a02: mov    %r15,-0x38(%rbp)    0x0000026b0a5d0a06: vmovdqu %xmm6,-0x48(%rbp)   0x0000026b0a5d0a0b: vmovdqu %xmm7,-0x58(%rbp)   0x0000026b0a5d0a10: vmovdqu %xmm8,-0x68(%rbp)   0x0000026b0a5d0a15: vmovdqu %xmm9,-0x78(%rbp)   0x0000026b0a5d0a1a: vmovdqu %xmm10,-0x88(%rbp)   0x0000026b0a5d0a22: vmovdqu %xmm11,-0x98(%rbp)   0x0000026b0a5d0a2a: vmovdqu %xmm12,-0xa8(%rbp)   0x0000026b0a5d0a32: vmovdqu %xmm13,-0xb8(%rbp)   0x0000026b0a5d0a3a: vmovdqu %xmm14,-0xc8(%rbp)   0x0000026b0a5d0a42: vmovdqu %xmm15,-0xd8(%rbp)

    const Address rdi_save(rbp, rdi_off * wordSize);     const Address rsi_save(rbp, rsi_off * wordSize);     __ movptr(rsi_save, rsi);     __ movptr(rdi_save, rdi);

  // rsi rdi   0x0000026b0a5d0a4a: mov    %rsi,-0x10(%rbp)   0x0000026b0a5d0a4e: mov    %rdi,-0x18(%rbp)

    // Load up thread register     __ movptr(r15_thread, thread);     __ reinit_heapbase();

  // 线程寄存器   0x0000026b0a5d0a52: mov    0x48(%rbp),%r15   0x0000026b0a5d0a56: movabs $0x7ffe4c5b2be8,%r10   0x0000026b0a5d0a60: mov    (%r10),%r12

    // pass parameters if any     BLOCK_COMMENT("pass parameters if any");     Label parameters_done;     __ movl(c_rarg3, parameter_size);     __ testl(c_rarg3, c_rarg3);     __ jcc(Assembler::zero, parameters_done);      Label loop;     __ movptr(c_rarg2, parameters);       // parameter pointer     __ movl(c_rarg1, c_rarg3);            // parameter counter is in c_rarg1     __ BIND(loop);     __ movptr(rax, Address(c_rarg2, 0));// get parameter     __ addptr(c_rarg2, wordSize);       // advance to next parameter     __ decrementl(c_rarg1);             // decrement counter     __ push(rax);                       // pass parameter     __ jcc(Assembler::notZero, loop);

  // 这里是个循环,用于传递参数,相当于   // while(r9d){   //    rax = *arg   //    push_arg(rax)   //    arg++;  // ptr++   //    r9d--;  // counter--   // }   0x0000026b0a5d0a63: mov    0x40(%rbp),%r9d   0x0000026b0a5d0a67: test   %r9d,%r9d   0x0000026b0a5d0a6a: je     0x0000026b0a5d0a83   0x0000026b0a5d0a70: mov    0x38(%rbp),%r8   0x0000026b0a5d0a74: mov    %r9d,%edx   0x0000026b0a5d0a77: mov    (%r8),%rax   0x0000026b0a5d0a7a: add    $0x8,%r8   0x0000026b0a5d0a7e: dec    %edx   0x0000026b0a5d0a80: push   %rax   0x0000026b0a5d0a81: jne    0x0000026b0a5d0a77

    // call Java function     __ BIND(parameters_done);     __ movptr(rbx, method);             // get Method*     __ movptr(c_rarg1, entry_point);    // get entry_point     __ mov(r13, rsp);                   // set sender sp     BLOCK_COMMENT("call Java function");     __ call(c_rarg1);

  // [!!]调用java方法   0x0000026b0a5d0a83: mov    0x28(%rbp),%rbx   0x0000026b0a5d0a87: mov    0x30(%rbp),%rdx   0x0000026b0a5d0a8b: mov    %rsp,%r13   0x0000026b0a5d0a8e: callq  *%rdx

    BLOCK_COMMENT("call_stub_return_address:");     return_address = __ pc();      // store result depending on type (everything that is not     // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)     __ movptr(c_rarg0, result);     Label is_long, is_float, is_double, exit;     __ movl(c_rarg1, result_type);     __ cmpl(c_rarg1, T_OBJECT);     __ jcc(Assembler::equal, is_long);     __ cmpl(c_rarg1, T_LONG);     __ jcc(Assembler::equal, is_long);     __ cmpl(c_rarg1, T_FLOAT);     __ jcc(Assembler::equal, is_float);     __ cmpl(c_rarg1, T_DOUBLE);     __ jcc(Assembler::equal, is_double);      // handle T_INT case     __ movl(Address(c_rarg0, 0), rax);      __ BIND(exit);      // pop parameters     __ lea(rsp, rsp_after_call);

  // 储存java方法返回值并弹出参数,这里弹出操作即移动一下rsp指针   0x0000026b0a5d0a90: mov    0x18(%rbp),%rcx   0x0000026b0a5d0a94: mov    0x20(%rbp),%edx   0x0000026b0a5d0a97: cmp    $0xc,%edx   0x0000026b0a5d0a9a: je     0x0000026b0a5d0b30   0x0000026b0a5d0aa0: cmp    $0xb,%edx   0x0000026b0a5d0aa3: je     0x0000026b0a5d0b30   0x0000026b0a5d0aa9: cmp    $0x6,%edx   0x0000026b0a5d0aac: je     0x0000026b0a5d0b35   0x0000026b0a5d0ab2: cmp    $0x7,%edx   0x0000026b0a5d0ab5: je     0x0000026b0a5d0b3b   0x0000026b0a5d0abb: mov    %eax,(%rcx)   0x0000026b0a5d0abd: lea    -0x1d8(%rbp),%rsp

    // restore regs belonging to calling function #ifdef _WIN64     // emit the restores for xmm regs     if (VM_Version::supports_evex()) {       for (int i = xmm_save_first; i <= last_reg; i++) {         __ vinsertf32x4(as_XMMRegister(i), as_XMMRegister(i), xmm_save(i), 0);       }     } else {       for (int i = xmm_save_first; i <= last_reg; i++) {         __ movdqu(as_XMMRegister(i), xmm_save(i));       }     } #endif     __ movptr(r15, r15_save);     __ movptr(r14, r14_save);     __ movptr(r13, r13_save);     __ movptr(r12, r12_save);     __ movptr(rbx, rbx_save);      __ movptr(rdi, rdi_save);     __ movptr(rsi, rsi_save);

  // 恢复之前保存的caller-save寄存器   0x0000026b0a5d0ac4: vmovdqu -0x48(%rbp),%xmm6   0x0000026b0a5d0ac9: vmovdqu -0x58(%rbp),%xmm7   0x0000026b0a5d0ace: vmovdqu -0x68(%rbp),%xmm8   0x0000026b0a5d0ad3: vmovdqu -0x78(%rbp),%xmm9   0x0000026b0a5d0ad8: vmovdqu -0x88(%rbp),%xmm10   0x0000026b0a5d0ae0: vmovdqu -0x98(%rbp),%xmm11   0x0000026b0a5d0ae8: vmovdqu -0xa8(%rbp),%xmm12   0x0000026b0a5d0af0: vmovdqu -0xb8(%rbp),%xmm13   0x0000026b0a5d0af8: vmovdqu -0xc8(%rbp),%xmm14   0x0000026b0a5d0b00: vmovdqu -0xd8(%rbp),%xmm15   0x0000026b0a5d0b08: mov    -0x38(%rbp),%r15   0x0000026b0a5d0b0c: mov    -0x30(%rbp),%r14   0x0000026b0a5d0b10: mov    -0x28(%rbp),%r13   0x0000026b0a5d0b14: mov    -0x20(%rbp),%r12   0x0000026b0a5d0b18: mov    -0x8(%rbp),%rbx   0x0000026b0a5d0b1c: mov    -0x18(%rbp),%rdi   0x0000026b0a5d0b20: mov    -0x10(%rbp),%rsi

    // restore rsp     __ addptr(rsp, -rsp_after_call_off * wordSize);      // return     __ pop(rbp);     __ ret(0);

  // 结束__call_stub_entry这个函数   0x0000026b0a5d0b24: add    $0x1d8,%rsp   0x0000026b0a5d0b2b: vzeroupper    0x0000026b0a5d0b2e: pop    %rbp   0x0000026b0a5d0b2f: retq

下面这段代码逻辑上属于之前的存储java方法的返回值,随便举个例子0x0000026b0a5d0b30这个地址正是之前存放java方法的代码段je     0x0000026b0a5d0b30所跳之处,只是放到了最后而已:(不过我也不知道为什么要放到这后面)

    // handle return types different from T_INT     __ BIND(is_long);     __ movq(Address(c_rarg0, 0), rax);     __ jmp(exit);      __ BIND(is_float);     __ movflt(Address(c_rarg0, 0), xmm0);     __ jmp(exit);      __ BIND(is_double);     __ movdbl(Address(c_rarg0, 0), xmm0);     __ jmp(exit);      return start;   }

  0x0000026b0a5d0b30: mov    %rax,(%rcx)   0x0000026b0a5d0b33: jmp    0x0000026b0a5d0abd   0x0000026b0a5d0b35: vmovss %xmm0,(%rcx)   0x0000026b0a5d0b39: jmp    0x0000026b0a5d0abd   0x0000026b0a5d0b3b: vmovsd %xmm0,(%rcx)   0x0000026b0a5d0b3f: jmpq   0x0000026b0a5d0abd

对照汇编看非常清晰,不过也可以看到它建立了栈帧结构,但它还是没有执行java代码,而是使用callq *rdx进行的,
这也是为什么它叫做stub的原因。
另外上面的栈帧里面内容比较多,[rsp+xx]存放什么内容啊这些比较难记,已经归纳好的结构可以参见代码注释:

  // Windowsx86_64平台   //   //    注意c_rargd 表示寄存器,method/result表示内存地址[rbp+d]   //   //    c_rarg0:   call wrapper address                   address   //    c_rarg1:   result                                 address   //    c_rarg2:   result type                            BasicType   //    c_rarg3:   method                                 Method*   //    48(rbp): (interpreter) entry point              address   //    56(rbp): parameters                             intptr_t*   //    64(rbp): parameter size (in words)              int   //    72(rbp): thread                                 Thread*   //   //     [ return_from_Java     ] <--- 这里执行callq调用java方法。压入返回地址,跳转到java方法,也就是说↑上面的部分就是java方法使用的栈帧了   //     [ argument word n      ] <--- 循环传递的java方法实参   //      ...   // -60 [ argument word 1      ]    // -59 [ saved xmm31          ] <--- rsp after_call   //     [ saved xmm16-xmm30    ]    // -27 [ saved xmm15          ]   //     [ saved xmm7-xmm14     ]   //  -9 [ saved xmm6           ]    //  -7 [ saved r15            ]   //  -6 [ saved r14            ]   //  -5 [ saved r13            ]   //  -4 [ saved r12            ]   //  -3 [ saved rdi            ]   //  -2 [ saved rsi            ]   //  -1 [ saved rbx            ]   //   0 [ saved rbp            ] <--- rbp   //   1 [ return address       ] <--- last rbp   //   2 [ call wrapper         ] <--- arg0   //   3 [ result               ] <--- arg1   //   4 [ result type          ] <--- arg2   //   5 [ method               ] <--- arg3   //   6 [ entry point          ] <--- arg4    //   7 [ parameters           ] <--- arg5   //   8 [ parameter size       ] <--- arg6   //   9 [ thread               ] <--- arg7

这8个arg正是之前传递给函数指针指向的函数的实参:

     StubRoutines::call_stub()(         (address)&link,         // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)         result_val_address,          // see NOTE above (compiler problem)         result_type,         method(),         entry_point,         args->parameters(),         args->size_of_parameters(),         CHECK       );