由于dispatch_next中执行需要用到解释器的分派表DispatchTable,它里面究竟存的是什么呢?那么本文本文就是模板解释器的模板解释器的分派表初始化过程\
TemplateInterpreter的initialize方法
//TemplateInterpreter定义的分派表
DispatchTable TemplateInterpreter::_active_table;
//模板解释器的初始化
void TemplateInterpreter::initialize() {
if (_code != NULL) return;
//AbstractInterpreter的初始化
AbstractInterpreter::initialize();
//模板表初始化
TemplateTable::initialize();
//生成模板解释器
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TraceStartupTime);
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,"Interpreter");
InterpreterGenerator g(_code);
if (PrintInterpreter) print();
}
// initialize dispatch table
_active_table = _normal_table;
}
复制代码
父类AbstractInterpreter的initialize的初始化。
1.主要判断_code(实际是StubQueue*类型)不为空,则表明已经初始化过,则直接返回, 然后就是性能统计的的类BytecodeCounter、BytecodeHistogram、BytecodePairHistogram、InvocationCounter的初始化。
void AbstractInterpreter::initialize() {
if (_code != NULL) return;
// make sure 'imported' classes are initialized
if (CountBytecodes || TraceBytecodes || StopInterpreterAt) BytecodeCounter::reset();
if (PrintBytecodeHistogram) BytecodeHistogram::reset();
if (PrintBytecodePairHistogram) BytecodePairHistogram::reset();
InvocationCounter::reinitialize(DelayCompilationDuringStartup );
}
复制代码
TemplateTable的initialize初始化模板表
void TemplateTable::initialize() {
if (_is_initialized) return;
// Initialize table
TraceTime timer("TemplateTable initialization", TraceStartupTime);
//初始化barrier集合
_bs = Universe::heap()->barrier_set();
const char _ = ' ';
const int ____ = 0;
const int ubcp = 1 << Template::uses_bcp_bit;
const int disp = 1 << Template::does_dispatch_bit;
const int clvm = 1 << Template::calls_vm_bit;
const int iswd = 1 << Template::wide_bit;
// interpr. templates
// Java spec bytecodes ubcp|disp|clvm|iswd in out generator argument
def(Bytecodes::_nop , ____|____|____|____, vtos, vtos, nop , _ );
def(Bytecodes::_aconst_null , ____|____|____|____, vtos, atos, aconst_null , _ );
// 省略中间字节码的指令的定义
def(Bytecodes::_shouldnotreachhere , ____|____|____|____, vtos, vtos, shouldnotreachhere , _ );
// platform specific bytecodes
pd_initialize();
_is_initialized = true;
}
复制代码
- uses_bcp_bit、does_dispatch_bit、calls_vm_bit、wide_bit这四个是Template类中定义,值分别为0、1、2、3,所以ubcp是1,disp是2,clvm是4,iswd是8
class Template {
private:
enum Flags {
uses_bcp_bit, // set if template needs the bcp pointing to bytecode
does_dispatch_bit, // set if template dispatches on its own
calls_vm_bit, // set if template calls the vm
wide_bit // set if template belongs to a wide instruction
};
}
复制代码
- 调用模板执行def定义函数\
- ubcp、disp、clvm、iswd这四个值前面已经介绍过了\
- flags & iswd赋值给bool类型is_wide变量,标识是否是宽指令\
- 根据上一步的is_wide,true则调用template_for_wide,返回TemplateTable的_template_table_wide数组变量的下标是Bytecodes::Code枚举的Template对象, false则调用template_for返回_template_table对应字节码枚举的值的Template对象.
- 调用Template对象的initialize设置对应的入口.
void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {
// should factor out these constants
const int ubcp = 1 << Template::uses_bcp_bit;
const int disp = 1 << Template::does_dispatch_bit;
const int clvm = 1 << Template::calls_vm_bit;
const int iswd = 1 << Template::wide_bit;
// 是否
bool is_wide = (flags & iswd) != 0;
//校验
assert(in == vtos || !is_wide, "wide instructions have vtos entry point only");
Template* t = is_wide ? template_for_wide(code) : template_for(code);
// setup entry
t->initialize(flags, in, out, gen, arg);
assert(t->bytecode() == code, "just checkin'");
}
复制代码
- Template对象的initialized初始化,分别初始化以下四个字段\
_flags 描述模板解释器的属性
_tos_in 在模板指令执行前缓存栈顶状态
_tos_out 在模板指令执行后缓存栈顶状态
generator 生成模板指令函数指针
_arg 模板指令生成的参数
class Template VALUE_OBJ_CLASS_SPEC {
//函数指针
typedef void (*generator)(int arg);
// describes interpreter template properties (bcp unknown)
int _flags;
TosState _tos_in; // tos cache state before template execution
TosState _tos_out; // tos cache state after template execution
generator _gen; // template code generator
int _arg; // argument for template code generator
void Template::initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg) {
_flags = flags;
_tos_in = tos_in;
_tos_out = tos_out;
_gen = gen;
_arg = arg;
}
}
复制代码
StubQueue创建并初始化
- InterpreterCodeSize是定义的常量,const static int InterpreterCodeSize = 224 * 1024,初始化code_size大小,
- 创建StubQueue对象并赋值给_code字段
- 调用InterpreterGenerator的构造函数,并传入_code参数生成对应指令代码.
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TraceStartupTime);
int code_size = InterpreterCodeSize;
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL, "Interpreter");
InterpreterGenerator g(_code);
if (PrintInterpreter) print();
}
复制代码
- InterpreterGenerator的构造函数,
- 首先初始化父类TemplateInterpreterGenerator构造函数,将_unimplemented_bytecode、_illegal_bytecode_sequence初始化为NULL,
- 调用generate_all方法生成模板指令
//InterpreterGenerator的构造函数
InterpreterGenerator::InterpreterGenerator(StubQueue* code): TemplateInterpreterGenerator(code) {
generate_all(); // down here so it can be "virtual"
}
//TemplateInterpreterGenerator构造函数
TemplateInterpreterGenerator::TemplateInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) {
_unimplemented_bytecode = NULL;
_illegal_bytecode_sequence = NULL;
}
复制代码
生成字节码指令所对应汇编指令
//TemplateInterpreterGenerator的generate_all方法
void TemplateInterpreterGenerator::generate_all() {
AbstractInterpreterGenerator::generate_all();
{ CodeletMark cm(_masm, "error exits");
_unimplemented_bytecode = generate_error_exit("unimplemented bytecode");
_illegal_bytecode_sequence = generate_error_exit("illegal bytecode sequence - method not verified");
}
//省略生成method entry(方法入口)、异常处理入口、返回值处理等代码
//设置字节码的入口
set_entry_points_for_all_bytes();
set_safepoints_for_all_bytes();
}
复制代码
- 父类AbstractInterpreterGenerator的generate_all方法
调用generate_slow_signature_handler生成签名的处理器的汇编指令代码的地址。
void AbstractInterpreterGenerator::generate_all() {
{ CodeletMark cm(_masm, "slow signature handler");
Interpreter::_slow_signature_handler = generate_slow_signature_handler();
}
}
复制代码
- 这里还是以x86_32位平台为例 \
首先调用CodeletMark的构造函数,创建一个CodeletMark对象,传入汇编器和描述字段,这里主要初始化几个重要的对象、
- _clet是InterpreterCodelet,获取AbstractInterpreter的_code(StubQueue类型)变量,并调用其request方法申请codelet_size大小是保存一段解释器的代码片段.
- _cb是CodeBuffer描述内存空间生成的汇编代码地址,
- _masm是InterpreterMacroAssembler类型,用于生成汇编代码。
class CodeletMark: ResourceMark {
private:
InterpreterCodelet* _clet;
InterpreterMacroAssembler** _masm;
CodeBuffer _cb;
public:
CodeletMark(
InterpreterMacroAssembler*& masm,
const char* description,
Bytecodes::Code bytecode = Bytecodes::_illegal):
_clet((InterpreterCodelet*)AbstractInterpreter::code()->request(codelet_size())),
_cb(_clet->code_begin(), _clet->code_size())
{ // request all space (add some slack for Codelet data)
assert (_clet != NULL, "we checked not enough space already");
// initialize Codelet attributes
_clet->initialize(description, bytecode);
// create assembler for code generation
masm = new InterpreterMacroAssembler(&_cb);
_masm = &masm;
}
复制代码
- 生成签名的handle的指令的地址.
- address entry = __ pc(); 获取指令的开始,
- __ mov(rcx, rsp); 栈顶rsp保存到rcx寄存器
- __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), rbx, rdi, rcx);调用虚拟机中InterpreterRuntime的slow_signature_handler的函数返返回的Handler地址.
- 执行ret(0)返回0。
address AbstractInterpreterGenerator::generate_slow_signature_handler() {
address entry = __ pc();
// rbx,: method
// rcx: temporary
// rdi: pointer to locals
// rsp: end of copied parameters area
__ mov(rcx, rsp);
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), rbx, rdi, rcx);
__ ret(0);
return entry;
}
复制代码
- 设置字节码的入口,首先遍历DispatchTable的length是256,也就是字节码指令最多只能定义256个,JAVA1.8定义的字节码指令是202个,
- 将0到256下标转换成Bytecodes::Code枚举类型,JVM的Bytecodes::Code就是定义JVM的字节码指令的枚举.
- 通过Bytecodes::is_defined(code)判断,就看枚举值是否在指令枚举中,如果是set_entry_points设置对应的字节码指令的入口,不存在,则调用set_unimplemented设置为没有实现的指令。
set_entry_points_for_all_bytes();
void TemplateInterpreterGenerator::set_entry_points_for_all_bytes() {
for (int i = 0; i < DispatchTable::length; i++) {
Bytecodes::Code code = (Bytecodes::Code)i;
if (Bytecodes::is_defined(code)) {
set_entry_points(code);
} else {
set_unimplemented(i);
}
}
}
复制代码
TemplateInterpreterGenerator的set_entry_points
- 设置字节码入口逻辑如下:
- 首先创建CodeletMark对象,传入参数分别是InterpreterMacroAssembler、byte code name 、Bytecode::code(字节码指令),
- 将定义bep、cep、sep、aep、iep、lep、fep、dep、vep、wep这个address变量初始化为_illegal_bytecode_sequence,将vep、wep这两个address变量初始化为_unimplemented_bytecode,
- 判断定义的指令,调用set_short_entry_points设置非wide指令到TemplateTable的_template_table这个Template类型的数组中,如果是定义的宽指令.则设置TemplateTable中_template_table_wide这个Template类型的数组中.
void TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code code) {
CodeletMark cm(_masm, Bytecodes::name(code), code);
address bep = _illegal_bytecode_sequence;
address cep = _illegal_bytecode_sequence;
address sep = _illegal_bytecode_sequence;
address aep = _illegal_bytecode_sequence;
address iep = _illegal_bytecode_sequence;
address lep = _illegal_bytecode_sequence;
address fep = _illegal_bytecode_sequence;
address dep = _illegal_bytecode_sequence;
address vep = _unimplemented_bytecode;
address wep = _unimplemented_bytecode;
// code for short & wide version of bytecode
if (Bytecodes::is_defined(code)) {
Template* t = TemplateTable::template_for(code);
assert(t->is_valid(), "just checking");
set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);
}
if (Bytecodes::wide_is_defined(code)) {
Template* t = TemplateTable::template_for_wide(code);
assert(t->is_valid(), "just checking");
set_wide_entry_point(t, wep);
}
// set entry points
EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, vep);
Interpreter::_normal_table.set_entry(code, entry);
Interpreter::_wentry_point[code] = wep;
}
复制代码
- 接下来主要看下非wide指令的entry point得设置.
- btos、ctos、stos这三种栈顶缓存状态都是使用itos,所以前三个是不使用的,
- 当栈顶缓存状态是atos,即是Object对象缓存时,将当前汇编指令生成器的CodeSection的end值就(pc()方法返回的地址)赋值给vep,然后将栈顶缓存弹出赋值给atos,然后调用generate_and_dispatch进行生成对应字节码的汇编指令.
//栈顶缓存的状态
enum TosState { // describes the tos cache contents
btos = 0, // byte, bool tos cached
ctos = 1, // char tos cached
stos = 2, // short tos cached
itos = 3, // int tos cached
ltos = 4, // long tos cached
ftos = 5, // float tos cached
dtos = 6, // double tos cached
atos = 7, // object cached
vtos = 8, // tos not cached
number_of_states,
ilgl // illegal state: should not occur
};
void TemplateInterpreterGenerator::set_short_entry_points(Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep) {
assert(t->is_valid(), "template must exist");
switch (t->tos_in()) {
case btos:
case ctos:
case stos:
ShouldNotReachHere(); // btos/ctos/stos should use itos.
break;
case atos: vep = __ pc(); __ pop(atos); aep = __ pc(); generate_and_dispatch(t); break;
case itos: vep = __ pc(); __ pop(itos); iep = __ pc(); generate_and_dispatch(t); break;
case ltos: vep = __ pc(); __ pop(ltos); lep = __ pc(); generate_and_dispatch(t); break;
case ftos: vep = __ pc(); __ pop(ftos); fep = __ pc(); generate_and_dispatch(t); break;
case dtos: vep = __ pc(); __ pop(dtos); dep = __ pc(); generate_and_dispatch(t); break;
case vtos: set_vtos_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep); break;
default : ShouldNotReachHere(); break;
}
}
复制代码
- 生成字节码的模板指令并分派执行下一条字节码指令.
- 调用Template参数t的does_dispatch方法判断该模板的标识是没有分派,第一次调用这个的Template返回false.
- 判断Template对象的保存指令是否是宽指令,如果是,则获取指令的length赋值给step,以便获取指令的地址。
- 如果tos_out等于ilgl(异常的栈顶状态),则将Template的tos_out(该变量是TemplateTable的initialize初始化)
- 调用dispatch_prolog方法,该方法是钩子方法,在X86平台上空的实现.
- 调用Template的generate并传入_masm(汇编解释器),生成对应代码指令.
- 最后调用 __ dispatch_epilog分配到下一条指令去执行.
void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
int step;
if (!t->does_dispatch()) {
step = t->is_wide() ? Bytecodes::wide_length_for(t->bytecode()) : Bytecodes::length_for(t->bytecode());
if (tos_out == ilgl) tos_out = t->tos_out();
//省略性能统计的代码
__ dispatch_prolog(tos_out, step);
}
// generate template
t->generate(_masm);
// advance
if (t->does_dispatch()) {
__ should_not_reach_here();
} else {
// dispatch to next bytecode
__ dispatch_epilog(tos_out, step);
}
}
复制代码
- Template的generate生成对应的字节码的汇编代码
- 首先赋值当前的Template的this指针赋值TemplateTable的_desc(Template*)变量.将传入InterpreterMacroAssembler变量masm赋值TemplateTable的_masm变量.
- 执行_gen(Tempalate初始化赋值)这个函数指针并传入_arg参数,例如 iconst_0的指令函数就是指向TemplateTable的iconst函数。
- 调用masm的flush方法,将生成字节码指令汇编代码更新到指令缓存。
void Template::generate(InterpreterMacroAssembler* masm) {
// parameter passing
TemplateTable::_desc = this;
TemplateTable::_masm = masm;
// code generation
_gen(_arg);
masm->flush();
}
复制代码
下面是iconst的指令函数,value是操作的参数,例如 iconst_0参数value就是0. 生成的汇编指令
- transition校验tos_in、tos_out状态是否正确.
- 如果value等于0,通过xor指令将rax寄存器重置为0,value不为0,则将value拷贝到rax寄存器。
void TemplateTable::iconst(int value) {
transition(vtos, itos);
if (value == 0) {
__ xorptr(rax, rax);
} else {
__ movptr(rax, value);
}
}
复制代码
- 字节码指令的汇编代码生成完成,加下来就是创建EntryPoint,传入之前定义9个地址,设置Interpreter的_normal_table(DispatchTable类型)数组调用set_entry设置对应code的EntryPoint(指令入口)设置Interpreter的_wentry_point(DispatchTable类型)的code的位置赋值wep地址。
EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, vep);
Interpreter::_normal_table.set_entry(code, entry);
Interpreter::_wentry_point[code] = wep;
复制代码
DispatchTable类是就是保存的二维地址数组,第一纬度是tos表示栈顶缓存的状态,就是上面的介绍的9中栈顶缓存状态,第二个纬度是指令的长度,对应的值是对应汇编指令的地址的入口地址.
class DispatchTable {
public:
enum { length = 1 << BitsPerByte }; // an entry point for each byte value (also for undefined bytecodes)
private:
address _table[number_of_states][length]; // dispatch tables, indexed by tosca and bytecode
}
//DispatchTable的set_entry函数的实现
void EntryPoint::set_entry(TosState state, address entry) {
assert(0 <= state && state < number_of_states, "state out of bounds");
_entry[state] = entry;
}
复制代码
- 在回到最开始TemplateInterpreter::initialize函数中最后一行代码,就是初始化分派表的,将初始化好_normal_table(DispatchTable类型)赋值_active_table(DispatchTable类型),这样就是完成了字节码指令的分派表的初始化.
// initialize dispatch table
_active_table = _normal_table;
复制代码
DispatchTable中address的二维数组如下:
总结
本文注重是分下TemplateInterpreter定义_active_table动态分派表的初始化过程,它是initialize函数中,然后初始化AbstractInterpreter,以及TemplateTable的初始化函数中出初始化了JAVA字节码指令中定义的200多个指令,每一条指令创建一个Template对象,根据是否是wide指令分别保存在TemplateTable的_template_table和_template_table_wide两个Template类型的数组中,然后根据Template对象中每一条指令执行_gen指令以及参数,利用InterpreterMacroAssembler生成对应的9种不同栈顶缓状态的字节码指令的汇编代码保存到CodeBuffer的CodeSection中,并把模板指令解释器的入口地址保存到DispatchTable类型的_normal_table二位分派表中,它最终是赋值文章开头提到的做做字节码指令分派用到TemplateInterpreter中定义的DispatchTable类型变量_active_table。