JVM的模板解释器DispatchTable分派表的初始化

由于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;
}
复制代码
  1. 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
  };
}
复制代码
  1. 调用模板执行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'");
}
复制代码
  1. 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();
  }
复制代码
  1. 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();
}
复制代码
  1. 父类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();
  }
}
复制代码
  1. 这里还是以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;
  }
复制代码
  1. 生成签名的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;
}
复制代码
  1. 设置字节码的入口,首先遍历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
  1. 设置字节码入口逻辑如下:
  • 首先创建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;
}
复制代码
  1. 接下来主要看下非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;
  }
}
复制代码
  1. 生成字节码的模板指令并分派执行下一条字节码指令.
  • 调用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);
  }
}
复制代码
  1. 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);
  }
}
复制代码
  1. 字节码指令的汇编代码生成完成,加下来就是创建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;
}
复制代码
  1. 在回到最开始TemplateInterpreter::initialize函数中最后一行代码,就是初始化分派表的,将初始化好_normal_table(DispatchTable类型)赋值_active_table(DispatchTable类型),这样就是完成了字节码指令的分派表的初始化.
 // initialize dispatch table
  _active_table = _normal_table;
复制代码

DispatchTable中address的二维数组如下: image.png

总结
本文注重是分下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。

猜你喜欢

转载自juejin.im/post/7075214724320722952