紧接着上一篇文章介绍generate_fixed_frame是生成java方法的栈帧固定栈帧部分,也即是JVM定义的动态链接的部分,在此之前JVM规范的本地变量表已经初始化完成了.
void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) {
// initialize fixed part of activation frame
__ push(rax); // save return address
__ enter(); // save old & set new rbp,
__ push(rsi); // set sender sp
__ push((int32_t)NULL_WORD); // leave last_sp as null
__ movptr(rsi, Address(rbx,Method::const_offset())); // get ConstMethod*
__ lea(rsi, Address(rsi,ConstMethod::codes_offset())); // get codebase
__ push(rbx); // save Method*
if (ProfileInterpreter) {
Label method_data_continue;
__ movptr(rdx, Address(rbx, in_bytes(Method::method_data_offset())));
__ testptr(rdx, rdx);
__ jcc(Assembler::zero, method_data_continue);
__ addptr(rdx, in_bytes(MethodData::data_offset()));
__ bind(method_data_continue);
__ push(rdx); // set the mdp (method data pointer)
} else {
__ push(0);
}
__ movptr(rdx, Address(rbx, Method::const_offset()));
__ movptr(rdx, Address(rdx, ConstMethod::constants_offset()));
__ movptr(rdx, Address(rdx, ConstantPool::cache_offset_in_bytes()));
__ push(rdx); // set constant pool cache
__ push(rdi); // set locals pointer
if (native_call) {
__ push(0); // no bcp
} else {
__ push(rsi); 、 // set bcp
}
__ push(0); // reserve word for pointer to expression stack bottom
__ movptr(Address(rsp, 0), rsp); // set expression stack bottom
}
- 将栈上的返回地址保存到rax寄存器中.
__ push(rax); // save return address
- enter方法会执行两条指令,
push(rbp); 保存当前rbp的栈基地址到栈中
mov(rbp, rsp);将当前rsp的值赋值给rbp寄存器,开辟一个新的栈帧\
__ enter();
- 保存rsi寄存器的值到栈上,rsi的值是调用者rsp栈顶。
__ push(rsi);
- 获取method的ConstMethod指针地址。
__ movptr(rsi, Address(rbx,Method::const_offset())); // get ConstMethod*
- 从ConstMethod取出字节码指令的偏移地址,从下面ConstMthod的codes_offset方法可以看出,ConstMethod将字节码指令放在ConstMethod对象的末尾。
__ lea(rsi, Address(rsi,ConstMethod::codes_offset())); // get codebase
// ConstMethod的方法
static ByteSize codes_offset(){
return in_ByteSize(sizeof(ConstMethod));
}
- 保存Method的指针地址到rbx寄存器.
__ push(rbx); // save Method*
- 是否开启解释器的性能统计数据,默认是false,这里暂时不展开讲了. 此段代码就会走else中代码,将往栈push一个0
if (ProfileInterpreter) {
Label method_data_continue;
__ movptr(rdx, Address(rbx, in_bytes(Method::method_data_offset())));
__ testptr(rdx, rdx);
__ jcc(Assembler::zero, method_data_continue);
__ addptr(rdx, in_bytes(MethodData::data_offset()));
__ bind(method_data_continue);
__ push(rdx); // set the mdp (method data pointer)
} else {
__ push(0);
}
- 将Method的ConstMethod的的地址移动到rdx寄存器.
__ movptr(rdx, Address(rbx, Method::const_offset()));
// Method的const_offset方法
static ByteSize const_offset(){ return byte_offset_of(Method, _constMethod ); }
- 将ConstMethod的ConstantPool的指针地址移动到rdx寄存器.
__ movptr(rdx, Address(rdx, ConstMethod::constants_offset()));
//ConstMethod的constants_offset方法
static ByteSize constants_offset()
{ return byte_offset_of(ConstMethod, _constants); }
- 将ConstantPool的ConstantPoolCache偏移地址移动到rdx寄存器.
__ movptr(rdx, Address(rdx, ConstantPool::cache_offset_in_bytes()));
// ConstantPool的cache_offset_in_bytes
static int cache_offset_in_bytes() { return offset_of(ConstantPool, _cache); }
- 保存rdx寄存器保存的是ConstantPoolCache的地址、rdi的寄存器保存的是第一个参数位置。
__ push(rdx); // set constant pool cache
__ push(rdi); // set locals pointer
- 这里先判断是否本地调用,如果不是,则表示是java调用,则保存byte code pointer(字节码指针)到栈顶,如果是本地方法是没有字节码指令,则直接保存一个0到栈顶.
if (native_call) {
__ push(0); // no bcp
} else {
__ push(rsi); // set bcp
}
- 再保存一个0到栈顶,作为保留字段,然后保存表达式栈的底部的值,
__ push(0); // reserve word for pointer to expression stack bottom
__ movptr(Address(rsp, 0), rsp); // set expression stack bottom
经过上面的步骤java的栈帧状态如下:
总结
本文主要分析了java生成固定栈帧的部分汇编指令逻辑,这样JVM规范定义的三部分中动态链接的栈帧也分析完了,还剩下操作数栈,hotspot中也称表达式栈,这个在接下来的一篇分析.