Since dispatch_next requires the interpreter’s DispatchTable, what does it contain? So this article is the template interpreter’s template interpreter dispatch table initialization procedure \
The Initialize method of TemplateInterpreter
//TemplateInterpreter defines the DispatchTable DispatchTable TemplateInterpreter::_active_table; / / template interpreter initialization void TemplateInterpreter: : initialize () {if (_code! = NULL) return; / / AbstractInterpreter initialization AbstractInterpreter: : initialize (); / / template table initialization TemplateTable: : initialize (); // Generate the template interpreter {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; }Copy the code
Initialization of the parent class AbstractInterpreter initialize.
1. If _code(StubQueue*) is not empty, it indicates that _code has been initialized. Directly return, and then initialize the performance statistics classes BytecodeCounter, BytecodeHistogram, BytecodePairHistogram, and 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 ); }Copy the code
TemplateTable initialize initializes the TemplateTable
void TemplateTable::initialize() { if (_is_initialized) return; // Initialize table TraceTime timer("TemplateTable initialization", TraceStartupTime); // initialize the barrier set _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 , _ ); / / omit intermediate bytecode instructions defined def (Bytecodes: : _shouldnotreachhere, ____ there comes there comes | | | ____, vtos, vtos, shouldnotreachhere, _); // platform specific bytecodes pd_initialize(); _is_initialized = true; }Copy the code
- Uses_bcp_bit, Does_dispatch_bit, calls_VM_bit and wide_bit are defined in Template class with values of 0, 1, 2 and 3 respectively, so UBCP is 1, DISp is 2, CLVM is 4 and ISwd is 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
};
}
Copy the code
- Call the template to execute the def definition function \
- The four values of ubcp, disp, CLVM, and iswd have been introduced previously
- Flags & iswd assigns the is_wide variable of type bool to indicate whether it is the wide instruction \
- True calls template_for_wide and returns the _template_table_wide array variable of TemplateTable with the Template object enumerated by Bytecodes::Code. False calls the template_for Template object that returns the value of the bytecode enumeration for _TEMPLate_table.
- Call initialize of the Template object to set the corresponding entry.
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; // whether bool is_wide = (flags & iswd)! = 0; / / check 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'"); }Copy the code
- Initialized for the Template object, initialize the following four fields \
_flags describes attributes of the template interpreter _tos_in Caches top stack state before template instruction execution _TOs_out Caches top stack state after template instruction execution Generator Generates template instruction function pointer _arg Parameters generated by template instruction
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; }}Copy the code
StubQueue is created and initialized
- Const static int InterpreterCodeSize = 224 * 1024; code_size;
- Create the StubQueue object and assign it to the _code field
- Call the InterpreterGenerator constructor and pass in the _code argument to generate the corresponding instruction 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();
}
Copy the code
- The constructor for the InterpreterGenerator,
- First initialize the parent class TemplateInterpreterGenerator constructor, _unimplemented_bytecode, _illegal_bytecode_sequence initialized to NULL,
- Call the generate_all method to generate the template directive
/ / InterpreterGenerator constructor InterpreterGenerator: : InterpreterGenerator (StubQueue * code) : TemplateInterpreterGenerator(code) { generate_all(); / / down here so it can be a "virtual"} / / TemplateInterpreterGenerator constructor TemplateInterpreterGenerator::TemplateInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) { _unimplemented_bytecode = NULL; _illegal_bytecode_sequence = NULL; }Copy the code
Generates the bytecode instruction corresponding to the assembly instruction
/ / TemplateInterpreterGenerator generate_all method of 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"); Set_entry_points_for_all_bytes (); set_entry_points_for_all_bytes(); set_safepoints_for_all_bytes(); }Copy the code
- The parent class AbstractInterpreterGenerator generate_all method
The address of the assembly instruction code of the processor that generates the signature by calling generate_slow_signature_handler.
void AbstractInterpreterGenerator::generate_all() { { CodeletMark cm(_masm, "slow signature handler"); Interpreter::_slow_signature_handler = generate_slow_signature_handler(); }}Copy the code
- Again, the x86_32-bit platform is used as an example
First call CodeletMark constructor, create a CodeletMark object, pass in the assembler and description field, initialize several important objects,
- _clet is a code snippet for InterpreterCodelet. AbstractInterpreter’s _code(type StubQueue) variable is retrieved and its Request method is called to request codelet_size.
- _cb is the address of assembly code generated by CodeBuffer describing memory space,
- _masm InterpreterMacroAssembler types, is used to generate assembly code.
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; }Copy the code
- The address of the handle instruction that generates the signature.
- address entry = __ pc(); Get the beginning of the instruction,
- __ mov(rcx, rsp); The stack top RSP is saved to the RCX register
- __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::slow_signature_handler), rbx, rdi, rcx); Slow_signature_handler: Slow_signature_handler (slow_signature_handler);
- Ret (0) returns 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;
}
Copy the code
- The length of DispatchTable is 256, that is, a maximum of 256 bytecode instructions can be defined. JAVA1.8 defines 202 bytecode instructions.
- Convert subscripts 0 to 256 to Bytecodes::Code enumeration. Bytecodes::Code for the JVM is the enumeration of bytecocode instructions that define the JVM.
- Bytecodes:: IS_defined (code) checks to see if the enumerated value is in the instruction enumeration. If set_entry_points is set to the entry for the corresponding bytecode instruction and it does not exist, set_unimplemented is called to set it to an instruction that is not implemented.
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); }}}Copy the code
The TemplateInterpreterGenerator set_entry_points
- Set the bytecode entry logic as follows:
- First create CodeletMark object, the incoming parameters are respectively InterpreterMacroAssembler, byte code name, the Bytecode: : code (Bytecode instruction),
- Initialize the address variable beP, Cep, sep, Aep, IEP, LEp, FEp, DEP, VEp, WEP to _illegal_bytecode_sequence. Initialize the address variables vep and WEp to _unimplemented_bytecode,
- To determine the defined directive, call set_short_entry_points to set the non-wide directive to an array of Template type _template_TABLE, if it is a defined wide directive. Set _template_table_wide in the TemplateTable array of Template type.
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;
}
Copy the code
- Next, let’s look at the setting of entry point for non-wide instructions.
- Btos, CTOS and STOS all use ITOS, so the first three are not used.
- When the state of the stack cache is ATOS, that is, the Object cache, the end value of the current assembly instruction generator’s CodeSection (the address returned by PC ()) is assigned to VEP, and the pop-up value of the stack cache is assigned to ATOS, and generate_AND_dispatch is called to generate Into the corresponding bytecode assembly instructions.
// 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; }}Copy the code
- Generates a bytecode template instruction and dispatches the execution of the next bytecode instruction.
- The does_dispatch method that calls Template parameter t determines that the Template’s identity is not dispatched, and the first call to this Template returns false.
- Determine if the save instruction of the Template object is a wide instruction. If so, get the length of the instruction and assign it to step to get the address of the instruction.
- If tos_out equals ILGL (exception top of stack state), then tos_out of Template (which is initialize of TemplateTable)
- Call the dispatch_prolog method, which is the hook method, an overhead implementation of the X86 platform.
- Call generate for Template and pass in _masm(assembly interpreter) to generate the corresponding code instruction.
- Finally, __ dispatch_epilog is assigned to the next instruction to execute.
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(); // omit performance statistics code __ 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); }}Copy the code
- Template’s generate generates assembly code for the corresponding bytecode
- First, assign the current Template’s this pointer to the TemplateTable variable _desc(Template*). Pass in masm InterpreterMacroAssembler variable assignment TemplateTable _masm variables.
- Call the _gen(Tempalate initialization) function pointer and pass in the _arg parameter. For example, the command function iconst_0 points to the iconst function in the TemplateTable.
- Call MASM’s Flush method to update the generated bytecode instruction assembly code to the instruction cache.
void Template::generate(InterpreterMacroAssembler* masm) {
// parameter passing
TemplateTable::_desc = this;
TemplateTable::_masm = masm;
// code generation
_gen(_arg);
masm->flush();
}
Copy the code
Here is the iconst instruction function, value is the operation parameter, for example, iconst_0 parameter value is 0. The generated assembly instruction
- Transition Verifies whether the TOS_IN and TOs_OUT states are correct.
- If value is 0, the RAX register is reset to 0 by the xOR instruction. If value is not 0, the value is copied to the RAX register.
void TemplateTable::iconst(int value) { transition(vtos, itos); if (value == 0) { __ xorptr(rax, rax); } else { __ movptr(rax, value); }}Copy the code
- The assembly code for the bytecode instructions is generated, plus the creation of EntryPoint, The Interpreter _normal_table(DispatchTable type) array is set. Set_entry is set to EntryPoint for the corresponding code. The Interpreter _wentry_point is set to wentry_point (DispatchTable type) to assign the WEP address.
EntryPoint entry(bep, cep, sep, aep, iep, lep, fep, dep, vep);
Interpreter::_normal_table.set_entry(code, entry);
Interpreter::_wentry_point[code] = wep;
Copy the code
The DispatchTable class is the stored two-dimensional address array. The first latitude is TOS representing the state of the top of the stack cache, which is the state of the top of the stack cache introduced in 9 above. The second latitude is the length of the instruction, and the corresponding value is the entry address of the address of the corresponding assembly instruction.
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, Void EntryPoint::set_entry(TosState state, address entry) { assert(0 <= state && state < number_of_states, "state out of bounds"); _entry[state] = entry; }Copy the code
- In the back to the beginning TemplateInterpreter: : initialize function in the last line of code, is the initial tumble sent table, Initialize _normal_table(DispatchTable type) and assign _active_table(DispatchTable type), thus completing the initialization of the DispatchTable of bytecode instructions.
// initialize dispatch table
_active_table = _normal_table;
Copy the code
DispatchTable address array is as follows:
The TemplateInterpreter definition _active_TABLE dynamically allocates the table initialization process in the initialize function, and then initializes AbstractInterpreter. And the TemplateTable initialization function, which initializes more than 200 instructions defined in JAVA bytecode instructions, each creating a Template object, Store the TemplateTable array _TEMPLate_TABLE and _TEMPLate_table_wide in the TemplateTable according to whether the wide directive is used. Then execute the _gen directive and parameters according to each directive in the Template object. The corresponding generated by InterpreterMacroAssembler slow state of nine different stack bytecode instructions to save assembly code to CodeBuffer CodeSection, and keep the template command interpreter entry address to _normal_ DispatchTable type The table 2-bit DispatchTable, which is the final assignment for the do bytecode instructions mentioned at the beginning of this article, dispatches using the DispatchTable type variable _active_table defined in TemplateInterpreter.