Repository navigation
runtime interp invokedynamic
ceragon edited this page Jun 22, 2022
·
1 revision
class ConstantPoolCacheEntry {
volatile intx _indices; // constant pool index & rewrite bytecodes
Metadata* volatile _f1; // entry specific metadata field
volatile intx _f2; // entry specific int/metadata field
volatile intx _flags; // flags
};void TemplateTable::invokedynamic(int byte_no) {
const Register rbx_method = rbx;
const Register rax_callsite = rax;
prepare_invoke(byte_no, rbx_method, rax_callsite);
__ jump_from_interpreted(rbx_method, 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 Bytecodes::Code code = bytecode();
const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
if (recv == noreg) recv = rcx;
if (flags == noreg) flags = rdx;
// __ save_bcp();
movptr(Address(rbp, frame::interpreter_frame_bcp_offset * wordSize), _bcp_register);
// method: rbx
// index: rax
// flags: rdx
load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
// method: ConstantPoolCacheEntry 的 f1 参数
// index: ConstantPoolCacheEntry 的 f2 参数
// flags: ConstantPoolCacheEntry 的 flags 参数
if (is_invokedynamic || is_invokehandle) {
Label L_no_push;
__ testl(flags, (1 << ConstantPoolCacheEntry::has_appendix_shift));
__ jcc(Assembler::zero, L_no_push);
__ push(rbx);
__ mov(rbx, index);
__ load_resolved_reference_at_index(index, rbx);
__ pop(rbx);
__ push(index); // push appendix (MethodType, CallSite, etc.)
__ bind(L_no_push);
}
__ 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);
}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;
size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
// cache: rcx
// index: rdx
// index_size: u4
resolve_cache_and_index(byte_no, cache, index, index_size);
// cache: 常量池的首地址
// index: 目标方法在常量池的索引
const int method_offset = in_bytes(
ConstantPoolCache::base_offset() +
((byte_no == f2_byte)
? ConstantPoolCacheEntry::f2_offset()
: ConstantPoolCacheEntry::f1_offset()));
// method: rbx
// method_offset: f1_offset
// 最后 method 会存储 目标方法的元数据首地址
__ movptr(method, Address(cache, index, Address::times_ptr, method_offset));
if (itable_index != noreg) {
// itable_index: rax
const int index_offset = in_bytes(ConstantPoolCache::base_offset() +
ConstantPoolCacheEntry::f2_offset());
// 访问常量池中,指定索引参数2 的值
// invokeDynamic 调用示例:
// 2: invokedynamic #6, 0 // InvokeDynamic #0:accept:(Lcom/test/hotreload/lambda/LambdaTest;)Ljava/util/function/Consumer;
__ movptr(itable_index, Address(cache, index, Address::times_ptr, index_offset));
}
// 取出方法在常量表中的 flags 参数
const int flags_offset = in_bytes(ConstantPoolCache::base_offset() +
ConstantPoolCacheEntry::flags_offset());
__ 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) {
const Register temp = rbx;
Label resolved;
Bytecodes::Code code = bytecode();
// Rcache: rcx
// index: rdx
// temp: rbx
// index_size: u4
__ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
// temp: 字节码对应的常量池索引中,ConstantPoolCacheEntry 的 _indices 值 (简单理解)
// cache: 常量池首地址
// index: 目标方法在常量池的索引
// 由于 _indices 存储了字节码的值,且用来表示方法是否已经完成了解析
__ cmpl(temp, code); // have we resolved this bytecode?
// 相等就是完成了解析
__ jcc(Assembler::equal, resolved);
// TODO: 暂时忽略解析步骤
// resolve first time through
address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
__ movl(temp, code);
__ call_VM(noreg, entry, temp);
// Update registers with resolved info
__ 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) {
// cache: rcx
// index: rdx
// bcp_offset: 1
// index_size: u4
get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size);
// cache: 常量池的首地址
// index: 目标方法在常量池的索引
// bytecode: rbx
// bytecode 最终是 ConstantPoolCacheEntry 的 _indices 值
// _indices: constant pool index & rewrite bytecodes
movl(bytecode, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()));
// byte_no = 1
// shift_count = 2 * 8 = 16
const int shift_count = (1 + byte_no) * BitsPerByte;
// 右移 16 位
shrl(bytecode, shift_count);
// bytecode_1_mask: 0xFF
// 取与
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: u4
get_cache_index_at_bcp(index, bcp_offset, index_size);
// index: 目标方法在常量表的索引
// cache: 常量池的首地址
movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize));
// 将 index 左移 2位置
shll(index, 2);
}
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index,
int bcp_offset,
size_t index_size) {
// index_size: u4
if (index_size == sizeof(u4)) {
// _bcp_register: method 中当前字节码的首地址
// 通过向上偏移 1 字节,得到调用目标方法在常量表的索引
movl(index, Address(_bcp_register, bcp_offset));
// TODO: 没太理解,总之最终 index 得到的是个索引
notl(index); // convert to plain index
}
}