Skip to content

runtime interp invokevirtual

ceragon edited this page Jun 13, 2022 · 7 revisions

解释执行 invoke virtual

主体代码

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

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

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

resolve_cache_and_index

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

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) {
    //  #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));
    }
}

get_cache_and_index_at_bcp

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

invokevirtual_helper

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

VM 调用

resolve_from_cache

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

Clone this wiki locally