iload会将int类型的本地变量推送至栈顶。模板定义如下:
def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ );
iload指令的格式如下:
iload index
index是一个无符号byte类型整数,指向局部变量表的索引值。
生成函数为TemplateTable::iload(),反编译后的汇编代码如下:
// 将%ebx指向下一条字节码指令的首地址 0x00007fffe1028d30: movzbl 0x2(%r13),%ebx // $0x15为_iload指令的操作码值 0x00007fffe1028d35: cmp $0x15,%ebx // 当下一条指令为iload时,直接跳转到done 0x00007fffe1028d38: je 0x00007fffe1028deb // done // 0xdf为_fast_iload指令的操作码值 0x00007fffe1028d3e: cmp $0xdf,%ebx // 将_fast_iload2指令移动到%ecx 0x00007fffe1028d44: mov $0xe0,%ecx 0x00007fffe1028d49: je 0x00007fffe1028d5a // rewrite // 0x34为_caload指令的操作码 // _caload指令表示从数组中加载一个char类型数据到操作数栈 0x00007fffe1028d4b: cmp $0x34,%ebx // 将_fast_icaload移动到%ecx中 0x00007fffe1028d4e: mov $0xe1,%ecx 0x00007fffe1028d53: je 0x00007fffe1028d5a // rewrite // 将_fast_iload移动到%ecx中 0x00007fffe1028d55: mov $0xdf,%ecx // -- rewrite -- // 调用patch_bytecode()函数 // 重写为fast版本,因为%cl中存储的是字节码的fast版本,%ecx的8位叫%cl 0x00007fffe1028de7: mov %cl,0x0(%r13) // -- done -- // 获取字节码指令的操作数,这个操作数为本地变量表的索引 0x00007fffe1028deb: movzbl 0x1(%r13),%ebx 0x00007fffe1028df0: neg %rbx // 通过本地变量表索引从本地变量表中加载值到%eax中, // %eax中存储的就是栈顶缓存值,所以不需要压入栈内 0x00007fffe1028df3: mov (%r14,%rbx,8),%eax
执行的逻辑如下:
假设现在有个方法的字节码指令流为连接3个iload指令,这3个iload指令前后都为非iload指令。那么重写的过程如下:
汇编代码在第一次执行时,如果判断最后一个_iload之后是非_iload指令,则会重写最后一个_iload指令为_fast_iload;第二次执行时,当第2个字节码指令为_iload,而之后接着判断为_fast_iload时,会更新第2个_iload为_fast_iload2。
执行_fast_iload和执行_fast_iload2都可以提高程序执行的效率,_fast_icaload指令也一样,下面详细介绍一下这几个指令。
1、_fast_iload指令
_fast_iload会将int类型的本地变量推送至栈顶。模板定义如下:
def(Bytecodes::_fast_iload , ubcp|____|____|____, vtos, itos, fast_iload , _ );
生成函数为TemplateTable::fast_iload() ,汇编代码如下:
0x00007fffe1023f90: movzbl 0x1(%r13),%ebx 0x00007fffe1023f95: neg %rbx 0x00007fffe1023f98: mov (%r14,%rbx,8),%eax
汇编代码很简单,这里不再过多说。
执行_fast_iload指令与执行_iload指令相比,不用再进行之前汇编介绍的那么多判断,也没有重写的逻辑,所以会提高执行效率。
2、_fast_iload2指令
_fast_iload2会将int类型的本地变量推送至栈顶。模板定义如下:
def(Bytecodes::_fast_iload2 , ubcp|____|____|____, vtos, itos, fast_iload2 , _ );
生成函数为TemplateTable::fast_iload2() ,汇编代码如下:
0x00007fffe1024010: movzbl 0x1(%r13),%ebx 0x00007fffe1024015: neg %rbx 0x00007fffe1024018: mov (%r14,%rbx,8),%eax 0x00007fffe102401c: push %rax 0x00007fffe102401d: movzbl 0x3(%r13),%ebx 0x00007fffe1024022: neg %rbx 0x00007fffe1024025: mov (%r14,%rbx,8),%eax
可以看到,此指令就相当于连续执行了2次iload指令,省去了指令跳转,所以效率要高一些。
3、_fast_icaload指令
caload指令表示从数组中加载一个char类型数据到操作数栈。
_fast_icaload会将char类型数组指定索引的值推送至栈顶。模板定义如下:
def(Bytecodes::_fast_icaload , ubcp|____|____|____, vtos, itos, fast_icaload , _ );
生成函数为TemplateTable::fast_icaload(),生成的汇编代码如下:
0x00007fffe1024090: movzbl 0x1(%r13),%ebx 0x00007fffe1024095: neg %rbx // %eax中存储着index 0x00007fffe1024098: mov (%r14,%rbx,8),%eax // %rdx中存储着arrayref 0x00007fffe102409c: pop %rdx // 将一个双字扩展后送到一个四字中,%rax中存储着index 0x00007fffe102409d: movslq %eax,%rax // %rdx指向数组对象的首地址,偏移0xc后获取length属性的值 0x00007fffe10240a0: cmp 0xc(%rdx),%eax 0x00007fffe10240a3: mov %eax,%ebx // 如果数组索引index等于数组的长度或大于数组长度时,那么跳转 // 到_throw_ArrayIndexOutOfBoundsException_entry抛出异常 0x00007fffe10240a5: jae 0x00007fffe100ff20 // 在指定数组arrayref中加载指定索引处index的值 0x00007fffe10240ab: movzwl 0x10(%rdx,%rax,2),%eax
可以看到,此指令省去了指令跳转,所以效率要高一些。