Skip to content

runtime interp invokedynamic

ceragon edited this page Jun 22, 2022 · 1 revision

invokeDynamic

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);
}

prepare_invoke

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);
}

load_invoke_cp_cache_entry

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);
}

get_cache_and_index_and_bytecode_at_bcp

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
    }
}

Clone this wiki locally