Skip to content

runtime interp stack

ceragon edited this page Jun 6, 2022 · 3 revisions

解释执行的栈结构

普通方法栈结构

索引 大小 名称 作用
0 8 save rax 保存返回地址
1 8 save rbp 保存栈帧的基址
=== = === ===
1 8 save rbcp(r13) 保存调用者的 rsp,产生方法调用那一刻时的 rsp
2 8 NULL_WORD 占位 (last_sp)
3 8 Method* (Method) 保存方法对象的地址
4 8 java mirror 方法所在的java class 对象地址
5 8 method data (Method->MethodData) 代码的首地址
6 8 constant pool cache 常量池的cache
7 8 locals pointer 局部变量表的地址
8 8 codes 代码地址
9 8 rsp 栈底的地址

定义

enum {
    pc_return_offset                                 =  0,
    // All frames
    link_offset                                      =  0,
    return_addr_offset                               =  1,
    // non-interpreter frames
    sender_sp_offset                                 =  2,

    // Interpreter frames
    interpreter_frame_result_handler_offset          =  3, // for native calls only
    interpreter_frame_oop_temp_offset                =  2, // for native calls only

    interpreter_frame_sender_sp_offset               = -1,
    // outgoing sp before a call to an invoked method
    interpreter_frame_last_sp_offset                 = interpreter_frame_sender_sp_offset - 1,
    interpreter_frame_method_offset                  = interpreter_frame_last_sp_offset - 1,
    interpreter_frame_mirror_offset                  = interpreter_frame_method_offset - 1,
    interpreter_frame_mdp_offset                     = interpreter_frame_mirror_offset - 1,
    interpreter_frame_cache_offset                   = interpreter_frame_mdp_offset - 1,
    interpreter_frame_locals_offset                  = interpreter_frame_cache_offset - 1,
    interpreter_frame_bcp_offset                     = interpreter_frame_locals_offset - 1,
    interpreter_frame_initial_sp_offset              = interpreter_frame_bcp_offset - 1,

    interpreter_frame_monitor_block_top_offset       = interpreter_frame_initial_sp_offset,
    interpreter_frame_monitor_block_bottom_offset    = interpreter_frame_initial_sp_offset,
    entry_frame_call_wrapper_offset                  =  2
};

栈帧涉及的对象

没有列出全部,只是一部分数据结构

Method

class Method : public Metadata {
    // 方法的只读数据
    ConstMethod*      _constMethod;
    MethodData*       _method_data;
    MethodCounters*   _method_counters;
    AccessFlags       _access_flags;
    
};

Method->ConstMethod

class ConstMethod : public MetaspaceObj {
    ConstantPool*     _constants;
    u2                _max_stack;
    u2                _max_locals;
    u2                _size_of_parameters;
};

Method->ConstMethod->ConstantPool

class ConstantPool : public Metadata {
    Array<u1>*           _tags;
    ConstantPoolCache*   _cache;
    InstanceKlass*       _pool_holder;
    Array<u2>*           _operands;
    int                  _length; // number of elements in the array
};
class ConstantPoolCacheEntry {
    volatile intx     _flags;    // flags
};

Method->MethodData

class MethodData : public Metadata {
    int _data_size;
    // Beginning of the data entries
    intptr_t _data[1];
};

InstanceKlass

class InstanceKlass: public Klass {
    Annotations*    _annotations;
    PackageEntry*   _package_entry;
    Klass* volatile _array_klasses;
    ConstantPool* _constants;
};

逐步解析方法执行过程的栈变化

address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
    __ pop(rax);
}

逐步解析方法调用过程的栈变化

只会贴出改变栈空间的指令

void TemplateTable::invokespecial(int byte_no) {
    prepare_invoke(byte_no, rbx, noreg,  // get f1 Method*
                 rcx);  // get receiver also for null check    
                 
}

修改 bcp

序号 名称 作用
0 rbp 栈帧基址
-8 bcp 发生方法调用的时候,用于存储方法字节码序列中产生调用字节码的首地址,例如 invokeSpecial
0 rbp 下一个方法的栈帧基址

先追溯一下 _bcp_register 的赋值

void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
    int step = 0;
    if (!t->does_dispatch()) {
        // 计算字节码的长度
        step = t->is_wide() ? 
                Bytecodes::wide_length_for(t->bytecode()) 
                : Bytecodes::length_for(t->bytecode());
    }
    // 生成字节码对应 native code
    t->generate(_masm);
    __ dispatch_epilog(tos_out, step);
}
void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) {
    dispatch_next(state, step);
}
void InterpreterMacroAssembler::dispatch_next(TosState state, int step, bool generate_poll) {
    // _bcp_register:方法中当前执行的字节码首地址
    // step: 当前字节码的大小
    // _bcp_register: 变为下一个字节码的首地址
    increment(_bcp_register, step);
}

下面是修改栈数据的地方

void TemplateTable::invokespecial(int byte_no) {
    prepare_invoke(byte_no, rbx, noreg,  // get f1 Method*
                 rcx);  // get receiver also for null check
}
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
                                   ) {
    //  __ save_bcp();
    // _bcp_register: r13
    // 将方法中当前字节码的首地址存到栈空间的指定问题
    movptr(Address(rbp, frame::interpreter_frame_bcp_offset * wordSize), _bcp_register);
}

保存返回地址

序号 名称 作用
0 rbp 栈帧基址
return address 返回地址
0 rbp 下一个方法的栈帧基址
void TemplateTable::invokespecial(int byte_no) {
    prepare_invoke(byte_no, rbx, noreg,  // get f1 Method*
                 rcx);  // get receiver also for null check
}
const int LogBytesPerInt     = 2;
const int LogBitsPerByte     = 3;
// 5
const int LogBitsPerInt      = LogBitsPerByte + LogBytesPerInt;
// 1 << 5 == 32
const int BitsPerInt         = 1 << LogBitsPerInt;
enum {
    tos_state_bits             = 4,
    // 32 - 4 = 28
    tos_state_shift            = BitsPerInt - tos_state_bits,
};
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
                                   ) {
    if (flags == noreg)  flags = rdx;
    // 加载常量池中的 flags 
    load_invoke_cp_cache_entry(byte_no, method, index, flags, is_invokevirtual, false, is_invokedynamic);
    // flags 右移 28 位
    __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
    {
        // 获取 return_entry_table 的地址
        const address table_addr = (address) Interpreter::invoke_return_entry_table_for(code);
        ExternalAddress table(table_addr);
        // 把地址加载到 rscratch1 中
        __ lea(rscratch1, table)
        // flags = [rscratch1 + (flags * Address::times_ptr)]
        __ movptr(flags, Address(rscratch1, flags, Address::times_ptr));
    }
    // push return address
    __ push(flags);
}

flags 的由来

Constant pool:
    #2 = Class              #25            // Test
    #4 = Methodref          #2.#26         // Test.print2:()V
    #9 = Utf8               ()V
    #21 = Utf8               print2
    #25 = Utf8               Test
    #26 = NameAndType        #21:#9         // print2:()V
invokevirtual #4                  // Method print2:()V
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;
    // 常量池有两个部分组成, cache 和 cacheEntry
    // 计算出 flags 所在的偏移
    const int flags_offset = in_bytes(ConstantPoolCache::base_offset() +
                                    ConstantPoolCacheEntry::flags_offset());
    // invokeSpecial 是 u2
    size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
    // 处理了 cache 的赋值
    resolve_cache_and_index(byte_no, cache, index, index_size);
    // cache: 常量池缓存的地址
    // index: 字节码指令后面的值,例如例子中的 #4。但会在上一步左移2位
    // flags:  (cache + index*3 + flags_offset) 地址对应的值
    // 看不懂变址寻址的结果,姑且认为是常量池里的 flags 吧
    __ movl(flags, Address(cache, index, Address::times_ptr, flags_offset));
}

cache和index 的由来

void TemplateTable::resolve_cache_and_index(int byte_no,
                                            Register Rcache,
                                            Register index,
                                            size_t index_size) {
    __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
}

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);
}
void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache,
                                                           Register index,
                                                           int bcp_offset,
                                                           size_t index_size) {
    // index = 字节码后面的参数
    get_cache_index_at_bcp(index, bcp_offset, index_size);
    // rbp: 当前的栈基址
    // cache: 常量池缓存的地址
    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)) {
        // invokeSpecial
        // 结合上下文,bcp_offset 是 1
        // index = (_bcp_register + 1) 地址里的值
        // 如上面的示例中, index=#4
        load_unsigned_short(index, Address(_bcp_register, bcp_offset));
    }
}

return_entry_table

address* TemplateInterpreter::invoke_return_entry_table_for(Bytecodes::Code code) {
    switch (code) {
        case Bytecodes::_invokespecial:
        return Interpreter::invoke_return_entry_table();
    }
}
class TemplateInterpreter: public AbstractInterpreter {
    static address _invoke_return_entry[number_of_return_addrs];           // for invokestatic, invokespecial, invokevirtual return entries
    static address*   invoke_return_entry_table()                 {
        return _invoke_return_entry; 
    }
};
address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, size_t index_size) {
    address entry = __ pc();
    __ movptr(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
    __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);
    __ restore_bcp();
    __ restore_locals();
    const Register cache = rbx;
    const Register index = rcx;
    __ get_cache_and_index_at_bcp(cache, index, 1, index_size);
    const Register flags = cache;
    __ movl(flags, Address(cache, index, Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()));
    __ andl(flags, ConstantPoolCacheEntry::parameter_size_mask);
    __ lea(rsp, Address(rsp, flags, Interpreter::stackElementScale()));
    __ dispatch_next(state, step);
    return entry;
}

Clone this wiki locally