在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屬性的值。
推薦閱讀:
第2篇-JVM虛擬機這樣來調用Java主類的main()方法
如果有問題可直接評論留言或加作者微信mazhimazh
關注公眾號,有HotSpot VM源碼剖析系列文章!