专注虚拟机与编译器研究

第21篇-加载与存储指令之iload、_fast_iload等(3)

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

可以看到,此指令省去了指令跳转,所以效率要高一些。

 

posted on 2021-09-29 14:59  鸠摩(马智)  阅读(529)  评论(0编辑  收藏  举报

导航