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
    load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
    if (load_receiver) {
        __ 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));
    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()));
    __ 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());
    __ 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;
    // index_size: u2
    __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
    __ cmpl(temp, code);
    __ jcc(Assembler::equal, resolved);
    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) {
    get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size);
    movl(bytecode, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()));
    const int shift_count = (1 + byte_no) * BitsPerByte;
    shrl(bytecode, shift_count);
    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) {
    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));
    }
}

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