hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
(他のオブジェクトのフィールドは read するだけなら値は変わらないので write 時にのみ SATB バリアを掛けている. 一方, java.lang.ref.Reference のフィールドは write されなくても消えることがある.
write だけでなく read もされてなければ不整合は起こらないが, write されずに read だけされ, しかも値が消えると不整合が起こりうる(と思われる). そのため, こいつだけは read 時にも SATB バリアを掛けている(模様).
なお, receiver が NULL の場合は, 値が取得できないので問題は出ない. このため除外している模様)
// Method entry for java.lang.ref.Reference.get.
address InterpreterGenerator::generate_Reference_get_entry(void) {
{- -------------------------------------------
(1) (この関数は使用する GC アルゴリズムが G1GC かどうかに応じて 2通りのコードを生成する)
---------------------------------------- -}
#ifndef SERIALGC
{- -------------------------------------------
(1) (この関数は, 以下のような典型的なアクセサメソッドの処理を高速化するためのもの.
_aload_0, _getfield, _areturn
生成するコードは, SATB 処理を行う部分と slow path の 2つに大別される.
G1 の場合は SATB 方式での concurrent marking 処理があるため,
Reference.get() 時には, receiver が NULL でなければ, referent フィールドの値を記録しておかなければいけない.
ただし, receiver が NULL の場合は, 明らかに値が取得できないので SATB バリアを実行する必要も無い)
---------------------------------------- -}
// Code: _aload_0, _getfield, _areturn
// parameter size = 1
//
// The code that gets generated by this routine is split into 2 parts:
// 1. The "intrinsified" code for G1 (or any SATB based GC),
// 2. The slow path - which is an expansion of the regular method entry.
//
// Notes:-
// * In the G1 code we do not check whether we need to block for
// a safepoint. If G1 is enabled then we must execute the specialized
// code for Reference.get (except when the Reference object is null)
// so that we can log the value in the referent field with an SATB
// update buffer.
// If the code for the getfield template is modified so that the
// G1 pre-barrier code is executed when the current method is
// Reference.get() then going through the normal method entry
// will be fine.
// * The G1 code can, however, check the receiver object (the instance
// of java.lang.Reference) and jump to the slow path if null. If the
// Reference object is null then we obviously cannot fetch the referent
// and so we don't need to call the G1 pre-barrier. Thus we can use the
// regular method entry code to generate the NPE.
//
// This code is based on generate_accessor_enty.
//
{- -------------------------------------------
(1) (生成したコードが実行される時点では, レジスタには以下の値が入っているはず)
---------------------------------------- -}
// rbx: methodOop
// r13: senderSP must preserve for slow path, set SP to it on fast path
{- -------------------------------------------
(1) (ここからが生成するコードの始まり)
---------------------------------------- -}
address entry = __ pc();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
const int referent_offset = java_lang_ref_Reference::referent_offset;
guarantee(referent_offset > 0, "referent offset not initialized");
{- -------------------------------------------
(1) (以下は G1GC の場合のパス)
---------------------------------------- -}
if (UseG1GC) {
{- -------------------------------------------
(1.1) (変数宣言など)
---------------------------------------- -}
Label slow_path;
// rbx: method
{- -------------------------------------------
(1.1) コード生成:
「第一引数(this)が NULL かどうかを確認する.
もし NULL だった場合は, slow_path ラベルにジャンプして通常処理にフォールバック.」
---------------------------------------- -}
// Check if local 0 != NULL
// If the receiver is null then it is OK to jump to the slow path.
__ movptr(rax, Address(rsp, wordSize));
__ testptr(rax, rax);
__ jcc(Assembler::zero, slow_path);
{- -------------------------------------------
(1.1) (この時点では, レジスタには以下の値が入っているはず)
---------------------------------------- -}
// rax: local 0
// rbx: method (but can be used as scratch now)
// rdx: scratch
// rdi: scratch
{- -------------------------------------------
(1.1) コード生成:
「第一引数(this)の Reference オブジェクトから
referent フィールドの値をロードする.」
---------------------------------------- -}
// Generate the G1 pre-barrier code to log the value of
// the referent field in an SATB buffer.
// Load the value of the referent field.
const Address field_address(rax, referent_offset);
__ load_heap_oop(rax, field_address);
{- -------------------------------------------
(1.1) コード生成:
「MacroAssembler::g1_write_barrier_pre() が生成するコードにより,
SATB 用の Write Barrier 処理を (その必要があれば) 実行する.」
(なお既に値はロード済みなので, MacroAssembler::g1_write_barrier_pre() が
ロード処理を生成しないように引数で指定している)
---------------------------------------- -}
// Generate the G1 pre-barrier code to log the value of
// the referent field in an SATB buffer.
__ g1_write_barrier_pre(noreg /* obj */,
rax /* pre_val */,
r15_thread /* thread */,
rbx /* tmp */,
true /* tosca_live */,
true /* expand_call */);
{- -------------------------------------------
(1.1) コード生成:
「SP を r13 の値に復帰させた後, リターン」
---------------------------------------- -}
// _areturn
__ pop(rdi); // get return address
__ mov(rsp, r13); // set sp to sender sp
__ jmp(rdi);
__ ret(0);
{- -------------------------------------------
(1.1) コード生成:
「receiver が NULL の場合は,
InterpreterGenerator::generate_normal_entry() が生成する通常パスにフォールバック」
---------------------------------------- -}
// generate a vanilla interpreter entry as the slow path
__ bind(slow_path);
(void) generate_normal_entry(false);
{- -------------------------------------------
(1.1) 以上で生成したコードの先頭アドレスをリターン.
---------------------------------------- -}
return entry;
}
#endif // SERIALGC
{- -------------------------------------------
(1) (以下は G1GC ではない場合のパス)
InterpreterGenerator::generate_accessor_entry() でコードを生成し, 結果をリターンするだけ.
---------------------------------------- -}
// If G1 is not enabled then attempt to go through the accessor entry point
// Reference.get is an accessor
return generate_accessor_entry();
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.