目录
3、generate_atomic_xchg和generate_disjoint_byte_copy
上篇《Hotspot 方法调用之StubRoutines 源码解析》中讲解了StubRoutines初始化相关的类,这篇文章就顺着其中StubGenerator_generate方法的实现来深入了解下JVM解释器相关类的使用。
一、StubGenerator
1、StubGenerator_generate
查看StubGenerator_generate方法的实现时,eclipse弹框如下图,即存在不同CPU架构的实现,我们重点关注x86_64的实现,这是后端CentOS服务器的常用的CPU架构。
那么编译器编译的时候怎么知道使用哪个文件的实现了?可以全局搜索包含stubGenerator_x86_64.cpp的文件,结果如下图:
点开这个configurations.xml,如下图:
其外层的xml的配置如下图:
即 configurations.xml指定了编译Linux-64版本时应该包含哪些文件。
StubGenerator_generate的源码实现如下,下面的几节会逐一讲解相关类的用途。
generate_initial和generate_all两个方法都是给StubRoutines中的static public的函数调用地址赋值,即生成stub,如下图:
2、定义
StubGenerator顾名思义就是用来生成Stub的,这里的Stub实际是一段可执行的汇编代码,具体来说就是生成StubRoutines中定义的多个public static的函数调用点,调用方可以将其作为一个经过优化后的函数直接使用。其类继承关系如下:
StubGenerator继承自StubCodeGenerator,但是没有添加任何新的属性,其对外的public方法只有一个构造方法,参考上节的StubGenerator_generate方法的源码。其他的都是用来生成单个Stub的私有方法,这些私有方法通过generate_initial和generate_all调用,如下图:
3、generate_atomic_xchg和generate_disjoint_byte_copy
generate_atomic_xchg生成的函数相当于jint atomic::xchg(jint exchange_value, volatile jint* dest),即原子的将exchange_value设置到dest指向的内存中,如果设置成功则返回原来的值。generate_disjoint_byte_copy用于来复制int或者byte数组。以这两个为例说明StubGenerator生成stub的实现。
generate_atomic_xchg的源码说明如下:
// Support for jint atomic::xchg(jint exchange_value, volatile jint* dest)
//
// Arguments :
// c_rarg0: exchange_value
// c_rarg1: dest
//
// Result:
// *dest <- ex, return (orig *dest)
address generate_atomic_xchg() {
//通过StubCodeMark的构造和析构函数来执行必要的资源初始化和销毁
StubCodeMark mark(this, "StubRoutines", "atomic_xchg");
//__是_masm->的别名,masm是StubCodeGenerator中的属性,MacroAssembler*,表示汇编代码的生成器
//pc()是MacroAssembler的方法,返回MacroAssembler绑定的CodeSection的end地址
address start = __ pc();
//movl()也是MacroAssembler的方法,对应movl汇编指令,其入参是两个Register,即寄存器
//rax和c_rarg0分别对应rax寄存器和通常用于保存第一个参数的rdi寄存器
//这里是将rdi寄存器中的数据拷贝到rax中
__ movl(rax, c_rarg0); // Copy to eax we need a return value anyhow
//将rax中的值同c_rarg1寄存器即rsi寄存器中保存的地址上的值原子的交换,如果交换成功,rax中的值就是原来dest的值
//可以先读取dest的值,然后再执行原子交换,如果交换的结果是之前读取的dest的值说明当前线程加锁成功
__ xchgl(rax, Address(c_rarg1, 0)); // automatic LOCK
//ret表示调用结束,返回
__ ret(0);
return start;
}
#define __ _masm->
generate_disjoint_byte_copy的源码说明如下:
// 本方法的参数:
// aligned - true => 如果为true,则表示输入输出都已经按照8字节对齐了
// name - stub的名字
// entry - 就是address*,无特别意义
//
// 调用此stub的参数:
// c_rarg0 - 原数组的地址
// c_rarg1 - 目标数组的地址
// c_rarg2 - 元素个数
//
// If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries,
// we let the hardware handle it. The one to eight bytes within words,
// dwords or qwords that span cache line boundaries will still be loaded
// and stored atomically.
//
// Side Effects:
// disjoint_byte_copy_entry is set to the no-overlap entry point
// used by generate_conjoint_byte_copy().
//
address generate_disjoint_byte_copy(bool aligned, address* entry, const char *name) {
//让Assamber关联的CodeSection按指定的内存大小对齐
__ align(CodeEntryAlignment);
//通过StubCodeMark的构造和析构函数执行初始化和善后处理工作
StubCodeMark mark(this, "StubRoutines", name);
//获取Assamber关联的CodeSection的end属性,即汇编代码写入的起始地址
address start = __ pc();
//Label表示跳转指令用到的标签
Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes;
Label L_copy_byte, L_exit;
//Register表示寄存器
const Register from = rdi; // 原数组地址
const Register to = rsi; // 目标数组地址
const Register count = rdx; // 元素个数
const Register byte_count = rcx;
const Register qword_count = count;
const Register end_from = from; // source array end address
const Register end_to = to; // destination array end address
// End pointers are inclusive, and if count is not zero they point
// to the last unit copied: end_to[0] := end_from[0]
__ enter(); // 往下移动rbp,开启一个新的栈帧
assert_clean_int(c_rarg2, rax); // 验证rdx寄存器中保存的数值是一个32位的int
if (entry != NULL) {
//将end属性赋值给entry
*entry = __ pc();
// 添加注释
BLOCK_COMMENT("Entry:");
}
//window下将r9和r10寄存器中的数据拷贝到rdi和rsi寄存器中,从而与Linux等保持一致
setup_arg_regs(); // from => rdi, to => rsi, count => rdx
// r9 and r10 may be used to save non-volatile registers
// 'from', 'to' and 'count' are now valid
//将count对应的rdx寄存器的值拷贝到byte_count对应的寄存器rcx中
__ movptr(byte_count, count);
//shr是逻辑右移指令,即将count的值右移3位
__ shrptr(count, 3); // count => qword_count
// Copy from low to high addresses. Use 'to' as scratch.
//lea指令用来加载有效地址到指定的寄存器
__ lea(end_from, Address(from, qword_count, Address::times_8, -8));
__ lea(end_to, Address(to, qword_count, Address::times_8, -8));
//neg指令用来对操作数取补,qword_count变成count的负数
__ negptr(qword_count); // make the count negative
//jmp指令用来跳转到指定的地址
__ jmp(L_copy_bytes);
// Copy trailing qwords
__ BIND(L_copy_8_bytes);
__ movq(rax, Address(end_from, qword_count, Address::times_8, 8));
__ movq(Address(end_to, qword_count, Address::times_8, 8), rax);
__ increment(qword_count);
__ jcc(Assembler::notZero, L_copy_8_bytes);
// Check for and copy trailing dword
__ BIND(L_copy_4_bytes);
__ testl(byte_count, 4);
__ jccb(A