hotspot/src/cpu/x86/vm/assembler_x86.cpp
void MacroAssembler::g1_write_barrier_pre(Register obj,
Register pre_val,
Register thread,
Register tmp,
bool tosca_live,
bool expand_call) {
{- -------------------------------------------
(1) (コメントによると,
expand_call 引数が true の場合には
InterpreterMacroAssember::call_VM_leaf_base() の代わりに
MacroAssember::call_VM_leaf_base() を使用している.
これは, InterpreterMacroAssembler::call_VM_leaf_base() が生成する
_last_sp のチェックコードを回避したい場合用,
とのこと)
---------------------------------------- -}
// If expand_call is true then we expand the call_VM_leaf macro
// directly to skip generating the check by
// InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp.
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
#ifdef _LP64
assert(thread == r15_thread, "must be");
#endif // _LP64
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Label done;
Label runtime;
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(pre_val != noreg, "check this code");
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
if (obj != noreg) {
assert_different_registers(obj, pre_val, tmp);
assert(pre_val != rax, "check this code");
}
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() +
PtrQueue::byte_offset_of_active()));
Address index(thread, in_bytes(JavaThread::satb_mark_queue_offset() +
PtrQueue::byte_offset_of_index()));
Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() +
PtrQueue::byte_offset_of_buf()));
{- -------------------------------------------
(1) コード生成:
「JavaThread::_satb_mark_queue フィールドの ObjPtrQueue オブジェクト内の
_active フィールドをチェックする.
もし _active フィールドの値が 0 (= false) であれば,
何もする必要が無いので, done ラベルまでジャンプ」
(このフィールドは, write barrier でポインタの記録が必要かどうかを示す.
ConcurrentMarkThread が動作中であれば true になる.
See: SATBMarkQueueSet::set_active_all_threads())
(なおコンパイラによって bool のバイト数が異なるので,
バイト数に応じて2通りのコードを生成する.
ここでは 4byte になる場合と 1byte になる場合を想定している模様)
---------------------------------------- -}
// Is marking active?
if (in_bytes(PtrQueue::byte_width_of_active()) == 4) {
cmpl(in_progress, 0);
} else {
assert(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption");
cmpb(in_progress, 0);
}
jcc(Assembler::equal, done);
{- -------------------------------------------
(1) コード生成:
「書き換え対象箇所の現在の値をロードする」
---------------------------------------- -}
// Do we need to load the previous value?
if (obj != noreg) {
load_heap_oop(pre_val, Address(obj, 0));
}
{- -------------------------------------------
(1) コード生成:
「もし現在の値が NULL であれば, 記録する価値はないので, done ラベルまでジャンプ」
---------------------------------------- -}
// Is the previous value null?
cmpptr(pre_val, (int32_t) NULL_WORD);
jcc(Assembler::equal, done);
{- -------------------------------------------
(1) コード生成:
「JavaThread::_satb_mark_queue フィールドの ObjPtrQueue オブジェクト内の
_index フィールドを tmp レジスタにロードし, 値をチェックする.
もし値が 0 の場合は, 次のバッファを用意する必要があるので, runtime ラベルにジャンプする.」
(なお, _index がバッファ中の現在の終端位置を示す. 値が 0 であればもう空き領域はない)
---------------------------------------- -}
// Can we store original value in the thread's buffer?
// Is index == 0?
// (The index field is typed as size_t.)
movptr(tmp, index); // tmp := *index_adr
cmpptr(tmp, 0); // tmp == 0?
jcc(Assembler::equal, runtime); // If yes, goto runtime
{- -------------------------------------------
(1) コード生成:
「tmp レジスタの値を wordSize 分だけ小さくし,
JavaThread::_satb_mark_queue フィールドの ObjPtrQueue オブジェクト内の
_index フィールドに書き戻す.」
---------------------------------------- -}
subptr(tmp, wordSize); // tmp := tmp - wordSize
movptr(index, tmp); // *index_adr := tmp
{- -------------------------------------------
(1) コード生成:
「JavaThread::_satb_mark_queue フィールドの ObjPtrQueue オブジェクト内の
_buf 内の _index の位置に保存対象の値(O0/I0レジスタの値)を書き込む.
(より具体的に言うと, tmp レジスタの値と buffer アドレスを足したアドレスに値を書き込む)」
---------------------------------------- -}
addptr(tmp, buffer); // tmp := tmp + *buffer_adr
// Record the previous value
movptr(Address(tmp, 0), pre_val);
{- -------------------------------------------
(1) コード生成:
「done ラベルにジャンプする (これで write barrier 処理は終了)」
---------------------------------------- -}
jmp(done);
{- -------------------------------------------
(1) (以降が runtime ラベルに飛んだ場合の処理)
---------------------------------------- -}
bind(runtime);
{- -------------------------------------------
(1) 必要に応じて, 生きているレジスタを待避するコードを生成しておく
---------------------------------------- -}
// save the live input values
if(tosca_live) push(rax);
if (obj != noreg && obj != rax)
push(obj);
if (pre_val != rax)
push(pre_val);
{- -------------------------------------------
(1) コード生成:
「SharedRuntime::g1_wb_pre() を呼び出し, バッファの確保及び write barrier 処理を行う.」
なお, この呼び出し処理は expand_call 引数に応じて少し異なる
* true の場合:
MacroAssembler::call_VM_leaf_base() が生成するコードによって行う.
* false の場合:
MacroAssembler::call_VM_leaf_base() をサブクラスがオーバーライドしたメソッドが生成するコードによって行う.
(InterpreterMacroAssember::call_VM_leaf_base())
(コメントによると, Reference.get() 用のルーチンの場合等にはスタックフレームがないせいで
InterpreterMacroAssember::call_VM_leaf_base() 内のランタイムチェックが失敗することがあるので,
呼び出し先を切り替えられるようにしている, とのこと)
(また, NOT_LP64 の場合には, 呼び出しの前後で thread 引数が示すレジスタの待避/復帰も行う)
---------------------------------------- -}
// Calling the runtime using the regular call_VM_leaf mechanism generates
// code (generated by InterpreterMacroAssember::call_VM_leaf_base)
// that checks that the *(ebp+frame::interpreter_frame_last_sp) == NULL.
//
// If we care generating the pre-barrier without a frame (e.g. in the
// intrinsified Reference.get() routine) then ebp might be pointing to
// the caller frame and so this check will most likely fail at runtime.
//
// Expanding the call directly bypasses the generation of the check.
// So when we do not have have a full interpreter frame on the stack
// expand_call should be passed true.
NOT_LP64( push(thread); )
if (expand_call) {
LP64_ONLY( assert(pre_val != c_rarg1, "smashed arg"); )
pass_arg1(this, thread);
pass_arg0(this, pre_val);
MacroAssembler::call_VM_leaf_base(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), 2);
} else {
call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), pre_val, thread);
}
NOT_LP64( pop(thread); )
{- -------------------------------------------
(1) 必要に応じて, 待避したレジスタを復帰させるコードを生成しておく.
---------------------------------------- -}
// save the live input values
if (pre_val != rax)
pop(pre_val);
if (obj != noreg && obj != rax)
pop(obj);
if(tosca_live) pop(rax);
{- -------------------------------------------
(1) (ここが done ラベルの位置)
---------------------------------------- -}
bind(done);
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.