[inside hotspot] 汇编模板解释器(Template Interpreter)和字节码执行
1.模板解释器
hotspot解释器模块(hotspot\src\share\vm\interpreter)有两个实现:基于C++的解释器和基于汇编的模板解释器。hotspot默认使用比较快的模板解释器。
其中
C++解释器 = bytecodeInterpreter* + cppInterpreter*
模板解释器 = templateTable* + templateInterpreter*
它们前者负责字节码的解释,后者负责解释器的运行时,共同完成解释功能。这里我们只关注模板解释器。
模板解释器又分为三个组成部分:
templateInterpreterGenerator 解释器生成器
templateTable 字节码实现
templateInterpreter 解释器
可能看起来很奇怪,为什么有一个解释器生成器和字节码实现。进入解释器实现:
class TemplateInterpreter: public AbstractInterpreter {
friend class VMStructs;
friend class InterpreterMacroAssembler;
friend class TemplateInterpreterGenerator;
friend class TemplateTable;
friend class CodeCacheExtensions;
// friend class Interpreter;
public:
enum MoreConstants {
number_of_return_entries = number_of_states,
// number of return entry points
number_of_deopt_entries = number_of_states,
// number of deoptimization entry points
number_of_return_addrs = number_of_states
// number of return addresses
};
protected:
static address _throw_ArrayIndexOutOfBoundsException_entry;
static address _throw_ArrayStoreException_entry;
static address _throw_ArithmeticException_entry;
static address _throw_ClassCastException_entry;
static address _throw_NullPointerException_entry;
static address _throw_exception_entry;
static address _throw_StackOverflowError_entry;
static address _remove_activation_entry;
// continuation address if an exception is not handled by current frame
#ifdef HOTSWAP
static address _remove_activation_preserving_args_entry; // continuation address when current frame is being popped
#endif // HOTSWAP
#ifndef PRODUCT
static EntryPoint _trace_code;
#endif // !PRODUCT
static EntryPoint _return_entry[number_of_return_entries]; // entry points to return to from a call
static EntryPoint _earlyret_entry;
// entry point to return early from a call
static EntryPoint _deopt_entry[number_of_deopt_entries];
// entry points to return to from a deoptimization
static EntryPoint _continuation_entry;
static EntryPoint _safept_entry;
static address _invoke_return_entry[number_of_return_addrs];
// for invokestatic, invokespecial, invokevirtual return entries
static address _invokeinterface_return_entry[number_of_return_addrs]; // for invokeinterface return entries
static address _invokedynamic_return_entry[number_of_return_addrs]; // for invokedynamic return entries
static DispatchTable _active_table;
// the active dispatch table (used by the interpreter for dispatch)
static DispatchTable _normal_table;
// the normal dispatch table (used to set the active table in normal mode)
static DispatchTable _safept_table;
// the safepoint dispatch table (used to set the active table for safepoints)
static address
_wentry_point[DispatchTable::length]; // wide instructions only (vtos tosca always)
public:
...
static int InterpreterCodeSize;
};
里面很多address变量,EntryPoint是一个address数组,DispatchTable也是。
模板解释器就是由一系列例程(routine)组成的,即address变量,它们每个都表示一个例程的入口地址,比如异常处理例程,invoke指令例程,用于gc的safepoint例程...
举个形象的例子,我们都知道字节码文件长这样:
public void f();
0: aload_0
1: invokespecial #5
// Method A.f:()V
4: getstatic
#2
// Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc
#6
// String ff
9: invokevirtual #4
// Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
如果要让我们写解释器,可能基本上就是一个循环里面switch,根据不同opcode派发到不同例程,例程的代码都是一样的模板代码,对aload_0的处理永远是取局部变量槽0的数据放到栈顶,那么完全可以在switch派发字节码前准备好这些模板代码,templateInterpreterGenerator就是做的这件事,它的generate_all()函数初始化了所有的例程:
void TemplateInterpreterGenerator::generate_all() {
// 设置slow_signature_handler例程
{ CodeletMark cm(_masm, "slow signature handler");
AbstractInterpreter::_slow_signature_handler = generate_slow_signature_handler();
}
// 设置error_exit例程
{ CodeletMark cm(_masm, "error exits");
_unimplemented_bytecode = generate_error_exit("unimplemented bytecode");
_illegal_bytecode_sequence = generate_error_exit("illegal bytecode sequence - method not verified");
}
......
}
另外,既然已经涉及到机器码了,单独的templateInterpreterGenerator显然是不能完成这件事的,它还需要配合
hotspot\src\cpu\x86\vm\templateInterpreterGenerator_x86.cpp&&hotspot\src\cpu\x86\vm\templateInterpreterGenerator_x86_64.cpp一起做事(我的机器是x86+windows)。