在TemplateInterpreterGenerator::generate_all()函式中生成了許多位元組碼指令以及一些虛擬機器輔助執行的機器指令片段,例如生成空指標異常丟擲入口的實現如下:

{
CodeletMark cm(_masm, "throw exception entrypoints");
// ...
Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException",NULL);
// ...
}

呼叫generate_exception_handler()函式生成丟擲空指標的程式碼片段。

address generate_exception_handler(const char* name, const char* message) {
return generate_exception_handler_common(name, message, false);
}

呼叫的generate_exception_handler_common()函式的實現如下:

address TemplateInterpreterGenerator::generate_exception_handler_common(
const char* name,
const char* message,
bool pass_oop
) { assert(!pass_oop || message == NULL, "either oop or message but not both");
address entry = __ pc();
if (pass_oop) {
// object is at TOS
__ pop(c_rarg2);
} // expression stack must be empty before entering the VM if an
// exception happened
__ empty_expression_stack(); // setup parameters
__ lea(c_rarg1, ExternalAddress((address)name)); if (pass_oop) {
__ call_VM(rax,
CAST_FROM_FN_PTR(address,InterpreterRuntime::create_klass_exception),
c_rarg1,c_rarg2);
} else {
// kind of lame ExternalAddress can't take NULL because
// external_word_Relocation will assert.
if (message != NULL) {
__ lea(c_rarg2, ExternalAddress((address)message));
} else {
__ movptr(c_rarg2, NULL_WORD);
}
__ call_VM(rax,
CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception),
c_rarg1, c_rarg2);
} // throw exception
__ jump(ExternalAddress(Interpreter::throw_exception_entry())); return entry;
}

生成的彙編程式碼如下:

0x00007fffe10101cb: mov    -0x40(%rbp),%rsp
0x00007fffe10101cf: movq $0x0,-0x10(%rbp)
0x00007fffe10101d7: movabs $0x7ffff6e09878,%rsi
0x00007fffe10101e1: movabs $0x0,%rdx
0x00007fffe10101eb: callq 0x00007fffe10101f5
0x00007fffe10101f0: jmpq 0x00007fffe1010288
0x00007fffe10101f5: lea 0x8(%rsp),%rax
0x00007fffe10101fa: mov %r13,-0x38(%rbp)
0x00007fffe10101fe: mov %r15,%rdi
0x00007fffe1010201: mov %rbp,0x200(%r15)
0x00007fffe1010208: mov %rax,0x1f0(%r15)
0x00007fffe101020f: test $0xf,%esp
0x00007fffe1010215: je 0x00007fffe101022d
0x00007fffe101021b: sub $0x8,%rsp
0x00007fffe101021f: callq 0x00007ffff66b3fbc
0x00007fffe1010224: add $0x8,%rsp
0x00007fffe1010228: jmpq 0x00007fffe1010232
0x00007fffe101022d: callq 0x00007ffff66b3fbc
0x00007fffe1010232: movabs $0x0,%r10
0x00007fffe101023c: mov %r10,0x1f0(%r15)
0x00007fffe1010243: movabs $0x0,%r10
0x00007fffe101024d: mov %r10,0x200(%r15)
0x00007fffe1010254: cmpq $0x0,0x8(%r15)
0x00007fffe101025c: je 0x00007fffe1010267
0x00007fffe1010262: jmpq 0x00007fffe1000420
0x00007fffe1010267: mov 0x250(%r15),%rax
0x00007fffe101026e: movabs $0x0,%r10
0x00007fffe1010278: mov %r10,0x250(%r15)
0x00007fffe101027f: mov -0x38(%rbp),%r13
0x00007fffe1010283: mov -0x30(%rbp),%r14
0x00007fffe1010287: retq
0x00007fffe1010288: jmpq 0x00007fffe100f3d3

在這裡的重點不是讀懂TemplateInterpreterGenerator::generate_exception_handler_common()函式的邏輯及生成的彙編程式碼,而是要清楚知道CodeletMark的應用,以及generate_exception_handler_common()函式生成的機器指令是如何寫入InterpreterCodelet例項中的。之前介紹過InterpreterCodelet與CodeBuffer類,如下:

通過CodeBuffer操作InterpreterCodelet例項的儲存機器指令片段的記憶體區域,而CodeBuffer中的程式碼部分(CodeSection)被賦值給AbstractAssembler::_code_section。這樣我們就可以通過_code_section屬性向InterpreterCodelet例項中寫入機器指令了。

向CodeletMark中傳入的_masm引數定義在AbstractInterpreterGenerator類中,如下:

class AbstractInterpreterGenerator: public StackObj {
protected:
InterpreterMacroAssembler* _masm;
// ...
}

generate_exception_handler_common()函式中的__是一個巨集,定義如下:

#define __ _masm->

這樣其實就是呼叫InterpreterMacroAssembler類中的相關函式寫機器指令,例如

__ pop(c_rarg2);

呼叫的pop()函式如下:

// 定義在InterpreterMacroAssembler中
void pop(Register r ) {
((MacroAssembler*)this)->pop(r);
} // 定義在Assembler類中
void Assembler::pop(Register dst) {
int encode = prefix_and_encode(dst->encoding());
emit_int8(0x58 | encode);
} // 定義在AbstractAssembler類中
void emit_int8( int8_t x) {
code_section()->emit_int8( x);
}

code_section()函式獲取的就是AbstractAssembler的_code_section屬性的值。  

推薦閱讀:

第1篇-關於JVM執行時,開篇說的簡單些

第2篇-JVM虛擬機器這樣來呼叫Java主類的main()方法

第3篇-CallStub新棧幀的建立

第4篇-JVM終於開始呼叫Java主類的main()方法啦

第5篇-呼叫Java方法後彈出棧幀及處理返回結果

第6篇-Java方法新棧幀的建立

第7篇-為Java方法建立棧幀

第8篇-dispatch_next()函式分派位元組碼

第9篇-位元組碼指令的定義

第10篇-初始化模板表

第11篇-認識Stub與StubQueue

第12篇-認識CodeletMark

如果有問題可直接評論留言或加作者微信mazhimazh

關注公眾號,有HotSpot VM原始碼剖析系列文章!