从iload_1 iload_2 iadd字节码角度看jvm字节码执行
ubuntu22 环境openjdk17
字节码执行流程分析
在Java字节码中,iload
指令用于从局部变量加载int值到操作数栈,iadd
指令用于将两个int值相加。在模板解释器中,执行字节码时会维护一个TosState(top-of-stack state),表示当前栈顶值的类型和位置(例如,vtos
表示栈顶值在内存栈上,itos
表示栈顶值在rax寄存器中)。
1. 第一个iload
(例如iload_1
)
字节码期望输入状态为
vtos
(栈顶无值在寄存器中),输出状态为itos
(值在rax寄存器中)。根据C++代码中的
iload
函数:cpp
transition(vtos, itos); __ movl(rax, iaddress(n)); // 将局部变量n的值加载到rax寄存器
执行后,值被加载到rax寄存器中,当前状态变为
itos
。
2. 第二个iload
(例如iload_2
)
第二个
iload
同样期望输入状态为vtos
,但当前状态是itos
(来自第一个iload
)。因此,需要将当前rax中的值推送到内存栈上,以将状态调整为vtos
。在C++代码的
set_vtos_entry_points
函数中,对于itos
状态,有:cpp
aep = bep = cep = sep = iep = __ pc(); // [abcsi]tos entry point __ push_i_or_ptr(); // 将rax中的整数值推送到栈上
推送后,状态变为
vtos
(值在栈上),然后执行iload_2
:cpp
transition(vtos, itos); __ movl(rax, iaddress(n)); // 加载新值到rax
执行后,栈上有一个值(来自第一个
iload
),rax寄存器中有新值(来自第二个iload
),状态变为itos
。
3. iadd
指令
iadd
期望输入状态为itos
(值在rax中),但需要另一个操作数从栈上弹出。根据C++代码中的
iop2
函数(用于整数操作):cpp
transition(itos, itos); switch (op) { case add : __ pop_i(rdx); __ addl (rax, rdx); break; }
__ pop_i(rdx)
从栈上弹出值到rdx寄存器(即第一个iload
推送的值),然后与rax寄存器中的值相加,结果保存在rax中,状态保持itos
。
用户理解的正确性
您的理解是正确的:
第一个
iload
将值加载到rax,状态变为itos
。第二个
iload
时,当前状态是itos
而不是期望的vtos
,因此将前一个值(rax中的值)push入栈(状态变为vtos
),然后加载新值到rax(状态又变为itos
)。iadd
执行时,pop出栈顶值到rdx寄存器,然后与rax相加。
补充说明
在模板解释器中,TosState的管理是为了优化性能,尽可能将值保留在寄存器中,减少内存访问。但当字节码期望的状态与当前状态不匹配时,需要通过push或pop操作进行转换。
##C++源码
def(Bytecodes::_iadd , ____|____|____|____, itos, itos, iop2 , add );def(Bytecodes::_iload_1 , ____|____|____|____, vtos, itos, iload , 1 );def(Bytecodes::_iload_2 , ____|____|____|____, vtos, itos, iload , 2 );void TemplateInterpreterGenerator::set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) {assert(t->is_valid(), "template must exist");switch (t->tos_in()) {case btos:case ztos:case ctos:case stos:ShouldNotReachHere(); // btos/ctos/stos should use itos.break;case atos: vep = __ pc(); __ pop(atos); aep = __ pc(); generate_and_dispatch(t); break;case itos: vep = __ pc(); __ pop(itos); iep = __ pc(); generate_and_dispatch(t); break;case ltos: vep = __ pc(); __ pop(ltos); lep = __ pc(); generate_and_dispatch(t); break;case ftos: vep = __ pc(); __ pop(ftos); fep = __ pc(); generate_and_dispatch(t); break;case dtos: vep = __ pc(); __ pop(dtos); dep = __ pc(); generate_and_dispatch(t); break;case vtos: set_vtos_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep); break;default : ShouldNotReachHere(); break;}
}void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t,address& bep,address& cep,address& sep,address& aep,address& iep,address& lep,address& fep,address& dep,address& vep) {assert(t->is_valid() && t->tos_in() == vtos, "illegal template");Label L;
#ifndef _LP64fep = __ pc(); // ftos entry point__ push(ftos);__ jmp(L);dep = __ pc(); // dtos entry point__ push(dtos);__ jmp(L);
#elsefep = __ pc(); // ftos entry point__ push_f(xmm0);__ jmp(L);dep = __ pc(); // dtos entry point__ push_d(xmm0);__ jmp(L);
#endif // _LP64lep = __ pc(); // ltos entry point__ push_l();__ jmp(L);aep = bep = cep = sep = iep = __ pc(); // [abcsi]tos entry point//yym-gaizao// 增加日志输出,打印即将压入栈的整数值// 假设整数值在 rax 寄存器中__ push(rbx); // 保存 rbx 寄存器__ push(rax); // 保存 rax 寄存器__ movptr(rbx, rax); // 将值移动到 rbx 以便保存__ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::print_int_value), rbx);__ pop(rax); // 恢复 rax 寄存器__ pop(rbx); // 恢复 rbx 寄存器__ push_i_or_ptr();vep = __ pc(); // vtos entry point__ bind(L);generate_and_dispatch(t);
}void TemplateTable::iop2(Operation op) {transition(itos, itos);switch (op) {case add : __ pop_i(rdx); __ addl (rax, rdx); break;case sub : __ movl(rdx, rax); __ pop_i(rax); __ subl (rax, rdx); break;case mul : __ pop_i(rdx); __ imull(rax, rdx); break;case _and : __ pop_i(rdx); __ andl (rax, rdx); break;case _or : __ pop_i(rdx); __ orl (rax, rdx); break;case _xor : __ pop_i(rdx); __ xorl (rax, rdx); break;case shl : __ movl(rcx, rax); __ pop_i(rax); __ shll (rax); break;case shr : __ movl(rcx, rax); __ pop_i(rax); __ sarl (rax); break;case ushr : __ movl(rcx, rax); __ pop_i(rax); __ shrl (rax); break;default : ShouldNotReachHere();}
}void TemplateTable::iload(int n) {transition(vtos, itos);__ movl(rax, iaddress(n));#if 0// 保存到 rax 的值即为 iload 加载的整数,我们希望记录它// yym-gaizao: 添加日志记录{__ push(rax); // 备份值__ movl(c_rarg0, rax); // 将值传递给参数1(c_rarg0)__ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::log_iload),c_rarg0);__ pop(rax); // 恢复寄存器}#endif
}void InterpreterMacroAssembler::pop(TosState state) {switch (state) {case atos: pop_ptr(); break;case btos:case ztos:case ctos:case stos:case itos: pop_i(); break;case ltos: pop_l(); break;case ftos: pop_f(xmm0); break;case dtos: pop_d(xmm0); break;case vtos: /* nothing to do */ break;default: ShouldNotReachHere();}interp_verify_oop(rax, state);
}#ifdef _LP64
void InterpreterMacroAssembler::pop_i(Register r) {// XXX can't use pop currently, upper half non cleanmovl(r, Address(rsp, 0));addptr(rsp, wordSize);
}void Assembler::push(int32_t imm32) {// in 64bits we push 64bits onto the stack but only// take a 32bit immediateemit_int8(0x68);emit_int32(imm32);
}
##志愿者
yym@yym:~/javaTest/javaByteCode$ cat ByteCodeTest.java
public class ByteCodeTest {public int add(int a, int b) {return a+b;}public int test() {int a = 2146598;int b = 1091754;int c = add(a, b);return c;}public static void main(String[] args) {ByteCodeTest byteCodeTest = new ByteCodeTest();byteCodeTest.test();}
}yym@yym:~/javaTest/javaByteCode$ javap -v ByteCodeTest
Classfile /home/yym/javaTest/javaByteCode/ByteCodeTest.classLast modified Sep. 19, 2025; size 459 bytesMD5 checksum 572a90d6c72910fade5618ef4133bc54Compiled from "ByteCodeTest.java"
public class ByteCodeTestminor version: 0major version: 55flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #5 // ByteCodeTestsuper_class: #8 // java/lang/Objectinterfaces: 0, fields: 0, methods: 4, attributes: 1
Constant pool:#1 = Methodref #8.#21 // java/lang/Object."<init>":()V#2 = Integer 2146598#3 = Integer 1091754#4 = Methodref #5.#22 // ByteCodeTest.add:(II)I#5 = Class #23 // ByteCodeTest#6 = Methodref #5.#21 // ByteCodeTest."<init>":()V#7 = Methodref #5.#24 // ByteCodeTest.test:()I#8 = Class #25 // java/lang/Object#9 = Utf8 <init>#10 = Utf8 ()V#11 = Utf8 Code#12 = Utf8 LineNumberTable#13 = Utf8 add#14 = Utf8 (II)I#15 = Utf8 test#16 = Utf8 ()I#17 = Utf8 main#18 = Utf8 ([Ljava/lang/String;)V#19 = Utf8 SourceFile#20 = Utf8 ByteCodeTest.java#21 = NameAndType #9:#10 // "<init>":()V#22 = NameAndType #13:#14 // add:(II)I#23 = Utf8 ByteCodeTest#24 = NameAndType #15:#16 // test:()I#25 = Utf8 java/lang/Object
{public ByteCodeTest();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public int add(int, int);descriptor: (II)Iflags: (0x0001) ACC_PUBLICCode:stack=2, locals=3, args_size=30: iload_11: iload_22: iadd3: ireturnLineNumberTable:line 4: 0public int test();descriptor: ()Iflags: (0x0001) ACC_PUBLICCode:stack=3, locals=4, args_size=10: ldc #2 // int 21465982: istore_13: ldc #3 // int 10917545: istore_26: aload_07: iload_18: iload_29: invokevirtual #4 // Method add:(II)I12: istore_313: iload_314: ireturnLineNumberTable:line 8: 0line 9: 3line 10: 6line 11: 13public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: new #5 // class ByteCodeTest3: dup4: invokespecial #6 // Method "<init>":()V7: astore_18: aload_19: invokevirtual #7 // Method test:()I12: pop13: returnLineNumberTable:line 15: 0line 16: 8line 17: 13
}