Repository navigation
runtime interp invokevirtual
ceragon edited this page Jun 13, 2022
·
7 revisions
void TemplateTable::invokevirtual(int byte_no) {
prepare_invoke(byte_no,
rbx, // method or vtable index
noreg, // unused itable index
rcx, rdx); // recv, flags
// rbx: index
// rcx: receiver
// rdx: flags
invokevirtual_helper(rbx, rcx, rdx);
}void TemplateTable::prepare_invoke(int byte_no,
Register method, // linked method (or i-klass)
Register index, // itable index, MethodType, etc.
Register recv, // if caller wants to see it
Register flags // if caller wants to test it
) {
const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
// recv: rcx
const bool load_receiver = (recv != noreg);
// flags: rdx
const bool save_flags = (flags != noreg);
// 保存当前字节码指令的首地址
__ save_bcp();
// index: noreg
// method: rbx, 方法的基址
// flags: rdx, 方法的参数(private, final 等)
load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
if (load_receiver) {
// recv: rcx
__ movl(recv, flags);
__ andl(recv, ConstantPoolCacheEntry::parameter_size_mask);
const int no_return_pc_pushed_yet = -1; // argument slot correction before we push return address
const int receiver_is_at_end = -1; // back off one slot to get receiver
Address recv_addr = __ argument_address(recv, no_return_pc_pushed_yet + receiver_is_at_end);
__ movptr(recv, recv_addr);
__ verify_oop(recv);
}
if (save_flags) {
__ movl(rbcp, flags);
}
__ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
{
const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code);
ExternalAddress table(table_addr);
__ lea(rscratch1, table);
__ movptr(flags, Address(rscratch1, flags, Address::times_ptr));
}
__ push(flags);
if (save_flags) {
__ movl(flags, rbcp);
__ restore_bcp();
}
}void TemplateTable::load_invoke_cp_cache_entry(int byte_no,
Register method,
Register itable_index,
Register flags,
bool is_invokevirtual,
bool is_invokevfinal, /*unused*/
bool is_invokedynamic) {
const Register cache = rcx;
const Register index = rdx;
// index_size = u2,也就是 short
size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
// byte_no: u2
// cache: 常量池的基址
// index: Methodref 的索引
resolve_cache_and_index(byte_no, cache, index, index_size);
const int method_offset = in_bytes(
ConstantPoolCache::base_offset() +
((byte_no == f2_byte)
? ConstantPoolCacheEntry::f2_offset()
: ConstantPoolCacheEntry::f1_offset()));
// method: rbx
// 根据 index 获取对应方法在常量池中的基址
__ movptr(method, Address(cache, index, Address::times_ptr, method_offset));
// itable_index = noreg
if (itable_index != noreg) {
// 不执行
}
const int flags_offset = in_bytes(ConstantPoolCache::base_offset() +
ConstantPoolCacheEntry::flags_offset());
// 获取方法的 flags 值(例如 private, final 等)
__ movl(flags, Address(cache, index, Address::times_ptr, flags_offset));
}void TemplateTable::resolve_cache_and_index(int byte_no,
Register Rcache,
Register index,
size_t index_size) {
// Rcache: rcx
// index: rdx
const Register temp = rbx;
Bytecodes::Code code = bytecode();
// index_size: u2
__ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
// Rcache: 常量池的基址
// index: Methodref 常量的索引
// temp: 如果 index 对应的类已经完成了解析,那么某些标志位会变成字节码的值
// 判断 temp 是否是 invokevirtual
__ cmpl(temp, code);
// 相等的话跳转到 resolved
__ jcc(Assembler::equal, resolved);
// TODO: 解析类
address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
__ movl(temp, code);
__ call_VM(noreg, entry, temp);
__ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
__ bind(resolved);
}void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register cache,
Register index,
Register bytecode,
int byte_no,
int bcp_offset,
size_t index_size) {
// #6 = Methodref #4.#38 // javap/Test.testHello:()V
// cache: rcx
// index: rdx
// bcp_offset: 1
// index_size: u2
get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size);
// bytecode: rbx
// cache: 常量池的基址
// index: 常量表的索引,例如 #6
// times_ptr: 值是3
// 将 index 对应的那个方法在常量池中的基址赋值到 rbx 中
movl(bytecode, Address(cache, index, Address::times_ptr,
ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()));
// 偏移数量, BitsPerByte == 0b1000 == 8;
// byte_no: f2_byte = 2
// shift_count = (1 + 2) * 8 = 24;
const int shift_count = (1 + byte_no) * BitsPerByte;
// 将地址右移 24位,
shrl(bytecode, shift_count);
// bytecode_1_mask: 0xFF
// bytecode: 应该是 #4 的值
andl(bytecode, ConstantPoolCacheEntry::bytecode_1_mask);
}
void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache,
Register index,
int bcp_offset,
size_t index_size) {
// index: rdx
// bcp_offset: 1
// index_size: u2
// 调用之后,index 将变成一个常量表的索引 (实际是上调用方法的常量信息)
get_cache_index_at_bcp(index, bcp_offset, index_size);
// 从栈上获取常量池的基址
movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize));
// index的值左移两位
shll(index, 2);
}
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index,
int bcp_offset,
size_t index_size) {
if (index_size == sizeof(u2)) {
// index: rdx
// _bcp_register: 当前字节码的首地址
// bcp_offset: 字节码第一个参数的偏移量
// 取出第一个参数,使用 load 是因为希望取出的值是无符号的 short,但是要放入一个8字节的寄存器里,于是高位需要补0
load_unsigned_short(index, Address(_bcp_register, bcp_offset));
}
}void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache,
Register index,
int bcp_offset,
size_t index_size) {
get_cache_index_at_bcp(index, bcp_offset, index_size);
movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize));
shll(index, 2);
}
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index,
int bcp_offset,
size_t index_size) {
if (index_size == sizeof(u2)) {
load_unsigned_short(index, Address(_bcp_register, bcp_offset));
}
}void TemplateTable::invokevirtual_helper(Register index,
Register recv,
Register flags) {
Label notFinal;
__ movl(rax, flags);
__ andl(rax, (1 << ConstantPoolCacheEntry::is_vfinal_shift));
__ jcc(Assembler::zero, notFinal);
const Register method = index; // method must be rbx
__ null_check(recv);
__ profile_final_call(rax);
__ profile_arguments_type(rax, method, rbcp, true);
__ jump_from_interpreted(method, rax);
__ bind(notFinal);
// get receiver klass
__ null_check(recv, oopDesc::klass_offset_in_bytes());
__ load_klass(rax, recv);
// profile this call
__ profile_virtual_call(rax, rlocals, rdx);
// get target Method* & entry point
__ lookup_virtual_method(rax, index, method);
__ profile_called_method(method, rdx, rbcp);
__ profile_arguments_type(rdx, method, rbcp, true);
__ jump_from_interpreted(method, rdx);
}void InterpreterRuntime::resolve_from_cache(JavaThread* thread, Bytecodes::Code bytecode) {
switch (bytecode) {
case Bytecodes::_invokevirtual:
resolve_invoke(thread, bytecode);
break;
}
}
void InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode) {
Thread* THREAD = thread;
LastFrameAccessor last_frame(thread);
Handle receiver(thread, NULL);
if (bytecode == Bytecodes::_invokevirtual || bytecode == Bytecodes::_invokeinterface ||
bytecode == Bytecodes::_invokespecial) {
methodHandle m (thread, last_frame.method());
Bytecode_invoke call(m, last_frame.bci());
Symbol* signature = call.signature();
receiver = Handle(thread, last_frame.callee_receiver(signature));
}
CallInfo info;
constantPoolHandle pool(thread, last_frame.method()->constants());
{
LinkResolver::resolve_invoke(info, receiver, pool,
last_frame.get_index_u2_cpcache(bytecode), bytecode, CHECK);
}
ConstantPoolCacheEntry* cp_cache_entry = last_frame.cache_entry();
if (cp_cache_entry->is_resolved(bytecode)) return;
switch (info.call_kind()) {
case CallInfo::direct_call:
cp_cache_entry->set_direct_call(
bytecode,
info.resolved_method(),
sender->is_interface());
break;
case CallInfo::vtable_call:
cp_cache_entry->set_vtable_call(
bytecode,
info.resolved_method(),
info.vtable_index());
break;
case CallInfo::itable_call:
cp_cache_entry->set_itable_call(
bytecode,
info.resolved_klass(),
info.resolved_method(),
info.itable_index());
break;
}
}