在前一篇 第3篇-CallStub新栈帧的创建 中我们介绍了generate_call_stub()函数的部分实现,完成了向CallStub栈帧中压入参数的操作,此时的状态如下图所示。
继续看generate_call_stub()函数的实现,接来下会加载线程寄存器,代码如下:
1 | scss复制代码__ movptr(r15_thread, thread); |
生成的汇编代码如下:
1 | perl复制代码mov 0x18(%rbp),%r15 |
对照着上面的栈帧可看一下0x18(%rbp)这个位置存储的是thread,将这个参数存储到%r15寄存器中。
如果在调用函数时有参数的话需要传递参数,代码如下:
1 | scss复制代码Label parameters_done; |
这里是个循环,用于传递参数,相当于如下代码:
1 | scss复制代码while(%esi){ |
生成的汇编代码如下:
1 | perl复制代码// 将栈中parameter size送到%ecx中 |
因为要调用Java方法,所以会为Java方法压入实际的参数,也就是压入parameter size个从parameters开始取的参数。压入参数后的栈如下图所示。
当把需要调用Java方法的参数准备就绪后,接下来就会调用Java方法。这里需要重点提示一下Java解释执行时的方法调用约定,不像C/C++在x86下的调用约定一样,不需要通过寄存器来传递参数,而是通过栈来传递参数的,说的更直白一些,是通过局部变量表来传递参数的,所以上图CallStub()函数栈帧中的argument word1 … argument word n其实是被调用的Java方法局部变量表的一部分。
下面接着看调用Java方法的代码,如下:
1 | scss复制代码// 调用Java方法 |
生成的汇编代码如下:
1 | perl复制代码// 将Method*送到%rbx中 |
注意调用callq指令后,会将callq指令的下一条指令的地址压栈,再跳转到第1操作数指定的地址,也就是*%rsi表示的地址。压入下一条指令的地址是为了让函数能通过跳转到栈上的地址从子函数返回。
callq指令调用的是entry_point。entry_point在后面会详细介绍。
公众号 深入剖析Java虚拟机HotSpot 已经更新虚拟机源代码剖析相关文章到60+,欢迎关注,如果有任何问题,可加作者微信mazhimazh,拉你入虚拟机群交流
本文转载自: 掘金