hotspot/src/cpu/sparc/vm/assembler_sparc.cpp
(この関数が生成するコードは O0 レジスタにアドレスが入っていると想定している)
// This gets to assume that o0 contains the object address.
static void generate_dirty_card_log_enqueue(jbyte* byte_map_base) {
{- -------------------------------------------
(1) BufferBlob::create() で BufferBlob を生成する.
---------------------------------------- -}
BufferBlob* bb = BufferBlob::create("dirty_card_enqueue", EnqueueCodeSize*2);
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
CodeBuffer buf(bb);
MacroAssembler masm(&buf);
address start = masm.pc();
Label not_already_dirty, restart, refill;
{- -------------------------------------------
(1) コード生成:
「書き込み先のアドレスに対応する card table 中の値を確認する.
まだ dirty (= 0) でなければ, not_already_dirty ラベルにジャンプして処理を行う.
(既に dirty になっている場合は, このままフォールスルーしてすぐにリターンするだけ)
(なお, ジャンプの遅延スロットで, card table 中の該当する箇所のアドレスを O3 レジスタにセットしている.
これは dirty でなかった場合の処理で使用される)」
(より具体的には,
まず, 書き込み先のアドレス(O0)の値を CardTableModRefBS::card_shift 分だけ右シフトさせて,
対応する card table 内の index を計算する.
次に, それに card table のアドレス(byte_map_base 引数の値)を足し込んだアドレスから
1byte ロードし, 0 と比較する)
(なお, 0 が dirty を示す (See: dirty_card))
---------------------------------------- -}
#ifdef _LP64
masm.srlx(O0, CardTableModRefBS::card_shift, O0);
#else
masm.srl(O0, CardTableModRefBS::card_shift, O0);
#endif
AddressLiteral addrlit(byte_map_base);
masm.set(addrlit, O1); // O1 := <card table base>
masm.ldub(O0, O1, O2); // O2 := [O0 + O1]
masm.br_on_reg_cond(Assembler::rc_nz, /*annul*/false, Assembler::pt,
O2, not_already_dirty);
// Get O1 + O2 into a reg by itself -- useful in the take-the-branch
// case, harmless if not.
masm.delayed()->add(O0, O1, O3);
{- -------------------------------------------
(1) コード生成:
「リターンする」 (これは, 既に dirty だった場合の処理)
---------------------------------------- -}
// We didn't take the branch, so we're already dirty: return.
// Use return-from-leaf
masm.retl();
masm.delayed()->nop();
{- -------------------------------------------
(1) (以下が, まだ dirty ではなかった場合の処理)
---------------------------------------- -}
// Not dirty.
masm.bind(not_already_dirty);
{- -------------------------------------------
(1) コード生成:
「card table 中の該当箇所に 0 を書き込んで dirty 化する」
---------------------------------------- -}
// First, dirty it.
masm.stb(G0, O3, G0); // [cardPtr] := 0 (i.e., dirty).
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
int dirty_card_q_index_byte_offset =
in_bytes(JavaThread::dirty_card_queue_offset() +
PtrQueue::byte_offset_of_index());
int dirty_card_q_buf_byte_offset =
in_bytes(JavaThread::dirty_card_queue_offset() +
PtrQueue::byte_offset_of_buf());
{- -------------------------------------------
(1) (ここが restart ラベルの位置. refill 処理を行った場合に処理をやり直す地点)
---------------------------------------- -}
masm.bind(restart);
{- -------------------------------------------
(1) コード生成:
「JavaThread::_dirty_card_queue フィールドの DirtyCardQueue オブジェクト内の
_index フィールドを L0 レジスタにロードする.
(ロード結果が 0 の場合は, 次のバッファを用意する必要があるので, refill ラベルにジャンプする).」
(なお, _index がバッファ中の現在の終端位置を示す. 値が 0 であればもう空き領域はない)
---------------------------------------- -}
masm.ld_ptr(G2_thread, dirty_card_q_index_byte_offset, L0);
masm.br_on_reg_cond(Assembler::rc_z, /*annul*/false, Assembler::pn,
L0, refill);
{- -------------------------------------------
(1) コード生成:
「JavaThread::_dirty_card_queue フィールドの DirtyCardQueue オブジェクト内の
_buf フィールドをロードする (なおこのロードは遅延スロットで実行される. ジャンプ先では使われないが害もない).」
---------------------------------------- -}
// If the branch is taken, no harm in executing this in the delay slot.
masm.delayed()->ld_ptr(G2_thread, dirty_card_q_buf_byte_offset, L1);
{- -------------------------------------------
(1) コード生成:
「L0 レジスタの値を oopSize 分だけ小さくしておく.」
---------------------------------------- -}
masm.sub(L0, oopSize, L0);
{- -------------------------------------------
(1) コード生成:
「JavaThread::_dirty_card_queue フィールドの DirtyCardQueue オブジェクト内の
_buf 内の "_index - oopSize" の位置に
card table 中の該当する箇所のアドレスを書き込む.
(より具体的に言うと, L0 と L1 を足したアドレスに O3 の値を書き込む)」
---------------------------------------- -}
masm.st_ptr(O3, L1, L0); // [_buf + index] := I0
{- -------------------------------------------
(1) コード生成:
「JavaThread::_dirty_card_queue フィールドの DirtyCardQueue オブジェクト内の
_index フィールドに, 新しい値(oopSize 分だけ小さくした値)を書き込む.
その後, リターンする.
(正確には, 書き込み処理はリターンの遅延スロットで行われる)」
---------------------------------------- -}
// Use return-from-leaf
masm.retl();
masm.delayed()->st_ptr(L0, G2_thread, dirty_card_q_index_byte_offset);
{- -------------------------------------------
(1) (以降が refill ラベルに飛んだ場合の処理)
---------------------------------------- -}
masm.bind(refill);
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
address handle_zero =
CAST_FROM_FN_PTR(address,
&DirtyCardQueueSet::handle_zero_index_for_thread);
{- -------------------------------------------
(1) (コメントによると
このケースは稀なので, 使用されている可能性のある全てのスクラッチレジスタを
ここで待避する程度は問題ないと想定している.
とのこと)
---------------------------------------- -}
// This should be rare enough that we can afford to save all the
// scratch registers that the calling context might be using.
{- -------------------------------------------
(1) コード生成:
「スクラッチ用の G レジスタを待避」
---------------------------------------- -}
masm.mov(G1_scratch, L3);
masm.mov(G3_scratch, L5);
{- -------------------------------------------
(1) コード生成:
「O0 レジスタの値が消えると困るので待避しておく.
また, O7 レジスタの値も消えると困るので待避しておく」
---------------------------------------- -}
// We need the value of O3 above (for the write into the buffer), so we
// save and restore it.
masm.mov(O3, L6);
// Since the call will overwrite O7, we save and restore that, as well.
masm.mov(O7, L4);
{- -------------------------------------------
(1) コード生成:
「MacroAssembler::call_VM_leaf() が生成するコードにより,
DirtyCardQueueSet::handle_zero_index_for_thread() を呼び出す.」
---------------------------------------- -}
masm.call_VM_leaf(L7_thread_cache, handle_zero, G2_thread);
{- -------------------------------------------
(1) コード生成:
「待避していたレジスタを復帰し, restart ラベルに戻って処理をやり直す.」
---------------------------------------- -}
masm.mov(L3, G1_scratch);
masm.mov(L5, G3_scratch);
masm.mov(L6, O3);
masm.br(Assembler::always, /*annul*/false, Assembler::pt, restart);
masm.delayed()->mov(L4, O7);
{- -------------------------------------------
(1) 以上で生成したコードの先頭アドレスと終端アドレスを
対応する大域変数にセットする.
---------------------------------------- -}
dirty_card_log_enqueue = start;
dirty_card_log_enqueue_end = masm.pc();
// XXX Should have a guarantee here about not going off the end!
// Does it already do so? Do an experiment...
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.