第13篇-通過InterpreterCodelet存儲機器指令片段


在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源碼剖析系列文章!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM