hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp
void TemplateTable::branch(bool is_jsr, bool is_wide) {
{- -------------------------------------------
(1) コード生成: (verify)
---------------------------------------- -}
// Note: on SPARC, we use InterpreterMacroAssembler::if_cmp also.
__ verify_oop(Lmethod);
__ verify_thread();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
const Register O2_bumped_count = O2;
{- -------------------------------------------
(1) コード生成: (JIT コンパイラ用のプロファイル情報の取得) (See: [here](no2935fdD.html) for details)
---------------------------------------- -}
__ profile_taken_branch(G3_scratch, O2_bumped_count);
{- -------------------------------------------
(1) コード生成:
「飛び先のオフセットを取得する」
---------------------------------------- -}
// get (wide) offset to O1_disp
const Register O1_disp = O1;
if (is_wide) __ get_4_byte_integer_at_bcp( 1, G4_scratch, O1_disp, InterpreterMacroAssembler::set_CC);
else __ get_2_byte_integer_at_bcp( 1, G4_scratch, O1_disp, InterpreterMacroAssembler::Signed, InterpreterMacroAssembler::set_CC);
{- -------------------------------------------
(1) コード生成: (jsr 用のコードを生成する場合)
jsr 用の場合は, 以下のようなコードを出力し, ここでリターン.
「リターンアドレスとして現在値を表す bci 値を算出し, オペランドスタックの先頭に乗せる.
さらに Lbcp を飛び先の値に変更する.
その後, dispatch_next() が生成するコードで,
次のバイトコードに対応するテンプレートへとジャンプする」
---------------------------------------- -}
// Handle all the JSR stuff here, then exit.
// It's much shorter and cleaner than intermingling with the
// non-JSR normal-branch stuff occurring below.
if( is_jsr ) {
// compute return address as bci in Otos_i
__ ld_ptr(Lmethod, methodOopDesc::const_offset(), G3_scratch);
__ sub(Lbcp, G3_scratch, G3_scratch);
__ sub(G3_scratch, in_bytes(constMethodOopDesc::codes_offset()) - (is_wide ? 5 : 3), Otos_i);
// Bump Lbcp to target of JSR
__ add(Lbcp, O1_disp, Lbcp);
// Push returnAddress for "ret" on stack
__ push_ptr(Otos_i);
// And away we go!
__ dispatch_next(vtos);
return;
}
{- -------------------------------------------
(1) (以下は, jsr ではない場合のコード生成)
---------------------------------------- -}
// Normal (non-jsr) branch handling
{- -------------------------------------------
(1) コード生成:
「Lbcp レジスタの値を一旦待避しておく」
---------------------------------------- -}
// Save the current Lbcp
const Register O0_cur_bcp = O0;
__ mov( Lbcp, O0_cur_bcp );
{- -------------------------------------------
(1) もし UseCompiler と UseLoopCounter の両方が指定されていれば,
以下の処理パスで backward branch 用の処理を行うコードを出力する.
---------------------------------------- -}
bool increment_invocation_counter_for_backward_branches = UseCompiler && UseLoopCounter;
if ( increment_invocation_counter_for_backward_branches ) {
{- -------------------------------------------
(1.1) コード生成:
「Lbcp の値を更新する (O1_disp に入っているオフセット分を足す).
さらに, 分岐の向きをチェックして,
forward branch であれば (backward branch 用の処理は不要なので) Lforward まで進む.」
(<= より正確に言うと, Lbcp の更新は分岐の遅延スロットを使って行っている)
---------------------------------------- -}
Label Lforward;
// check branch direction
__ br( Assembler::positive, false, Assembler::pn, Lforward );
// Bump bytecode pointer by displacement (take the branch)
__ delayed()->add( O1_disp, Lbcp, Lbcp ); // add to bc addr
{- -------------------------------------------
(1.1) TieredCompilation が指定されている場合, 以下の処理パスでコードを生成する
---------------------------------------- -}
if (TieredCompilation) {
{- -------------------------------------------
(1.1.1) (変数宣言など)
---------------------------------------- -}
Label Lno_mdo, Loverflow;
int increment = InvocationCounter::count_increment;
int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift;
{- -------------------------------------------
(1.1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には生成しない)
「もし methodDataOopDesc(mdo) が付いていれば,
InterpreterMacroAssembler::increment_mask_and_jump() が生成するコードによって
mdo の _backedge_counter フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
閾値に達していなかった場合は, Lforward ラベルにジャンプする.
そうでなければ, Loverflow ラベルまでジャンプする.
もし methodDataOopDesc が付いていなければ,
Lno_mdo ラベルまでジャンプする」
---------------------------------------- -}
if (ProfileInterpreter) {
// If no method data exists, go to profile_continue.
__ ld_ptr(Lmethod, methodOopDesc::method_data_offset(), G4_scratch);
__ br_null(G4_scratch, false, Assembler::pn, Lno_mdo);
__ delayed()->nop();
// Increment backedge counter in the MDO
Address mdo_backedge_counter(G4_scratch, in_bytes(methodDataOopDesc::backedge_counter_offset()) +
in_bytes(InvocationCounter::counter_offset()));
__ increment_mask_and_jump(mdo_backedge_counter, increment, mask, G3_scratch, Lscratch,
Assembler::notZero, &Lforward);
__ ba(false, Loverflow);
__ delayed()->nop();
}
{- -------------------------------------------
(1.1.1) コード生成:
「(ここが Lno_mdo ラベルの位置)」
---------------------------------------- -}
// If there's no MDO, increment counter in methodOop
__ bind(Lno_mdo);
{- -------------------------------------------
(1.1.1) (変数宣言など)
---------------------------------------- -}
Address backedge_counter(Lmethod, in_bytes(methodOopDesc::backedge_counter_offset()) +
in_bytes(InvocationCounter::counter_offset()));
{- -------------------------------------------
(1.1.1) コード生成:
「InterpreterMacroAssembler::increment_mask_and_jump() が生成するコードによって
実行中のメソッドに対応する methodOopDesc の
_backedge_counter フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
閾値に達していなかった場合は, Lforward ラベルにジャンプする.
そうでなければ, このままフォールスルーする」
---------------------------------------- -}
__ increment_mask_and_jump(backedge_counter, increment, mask, G3_scratch, Lscratch,
Assembler::notZero, &Lforward);
{- -------------------------------------------
(1.1.1) コード生成:
「(ここが Loverflow ラベルの位置)」
---------------------------------------- -}
__ bind(Loverflow);
{- -------------------------------------------
(1.1.1) コード生成:
「InterpreterRuntime::frequency_counter_overflow() を呼び出して JIT コンパイル処理を開始する.」
---------------------------------------- -}
// notify point for loop, pass branch bytecode
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), O0_cur_bcp);
{- -------------------------------------------
(1.1.1) コード生成:
「もし InterpreterRuntime::frequency_counter_overflow() の返り値が NULL だったら,
(まだ JIT 生成されたコードがないということなので) Lforward ラベルまで飛ぶ.
また, nmethod が既に invalidate されていた場合も, Lforward ラベルまで飛ぶ.」
---------------------------------------- -}
// Was an OSR adapter generated?
// O0 = osr nmethod
__ br_null(O0, false, Assembler::pn, Lforward);
__ delayed()->nop();
// Has the nmethod been invalidated already?
__ ld(O0, nmethod::entry_bci_offset(), O2);
__ cmp(O2, InvalidOSREntryBci);
__ br(Assembler::equal, false, Assembler::pn, Lforward);
__ delayed()->nop();
{- -------------------------------------------
(1.1.1) (ここまで来たら, JIT 生成された nmethod が得られたということになる)
---------------------------------------- -}
// migrate the interpreter frame off of the stack
{- -------------------------------------------
(1.1.1) コード生成:
「SharedRuntime::OSR_migration_begin() を呼んで,
interpreter frame 中の局所変数とモニタを C ヒープ上に取ったバッファに退避する.
(呼び出しの前後で G2_thread や O0(に入っている nmethod) の退避復帰を行っている)」
---------------------------------------- -}
__ mov(G2_thread, L7);
// save nmethod
__ mov(O0, L6);
__ set_last_Java_frame(SP, noreg);
__ call_VM_leaf(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), L7);
__ reset_last_Java_frame();
__ mov(L7, G2_thread);
{- -------------------------------------------
(1.1.1) コード生成:
「nmethod や SharedRuntime::OSR_migration_begin() が返したバッファを I0/I1 に退避してから,
restore 命令で interpreter frame を削除する.」
---------------------------------------- -}
// move OSR nmethod to I1
__ mov(L6, I1);
// OSR buffer to I0
__ mov(O0, I0);
// remove the interpreter frame
__ restore(I5_savedSP, 0, SP);
{- -------------------------------------------
(1.1.1) コード生成:
「nmethod からエントリポイントを取得し, osr コードにジャンプする.」
---------------------------------------- -}
// Jump to the osr code.
__ ld_ptr(O1, nmethod::osr_entry_point_offset(), O2);
__ jmp(O2, G0);
__ delayed()->nop();
{- -------------------------------------------
(1.1) TieredCompilation が指定されていない場合, 以下の処理パスでコードを生成する
---------------------------------------- -}
} else {
{- -------------------------------------------
(1.1.1) コード生成: (TieredCompilation が指定されていない場合)
まず, 以下のコードを生成する.
「InterpreterMacroAssembler::increment_backedge_counter() が生成するコードで
backedge_counter のカウント値をインクリメントする.」
その後, ProfileInterpreter が指定されていれば, 以下のコードを生成する.
「InterpreterMacroAssembler::test_invocation_counter_for_mdp() が生成するコードで
methodDataOop(mdp) に関するチェックを行う.
チェック結果に応じて, 以下の3通りに分岐.
* 既に methodDataOop が作成済みの場合:
このままフォールスルー
(UseOnStackReplacement が指定されていれば,
InterpreterMacroAssembler::test_backedge_count_for_osr() が生成するコードに突入する)
* 監視対象になる閾値を超えているが, まだ mdp が確保されていない場合:
新しい methodDataOop オブジェクトを methodOop 内にセットし, Lforward ラベルまで分岐.
* まだ監視対象になる閾値を超えていない場合:
Lforward ラベルまで分岐.」
その後, UseOnStackReplacement が指定されていれば, 以下のコードを生成する.
「InterpreterMacroAssembler::test_backedge_count_for_osr() が生成するコードで,
カウント値と閾値を比較し, 閾値を超えていれば JIT コンパイルを行う.
あるいは, 既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する.」
(なお, InterpreterMacroAssembler::test_backedge_count_for_osr() でチェックされるカウント値は,
ProfileInterpreter が有効な場合は O2_bumped_count レジスタ.
これは InterpreterMacroAssembler::profile_taken_branch() が生成するコードがセットしている.
ProfileInterpreter が無効な場合は G4_invoke_ctr レジスタ.
これは InterpreterMacroAssembler::increment_backedge_counter() が生成するコードがセットしている)
---------------------------------------- -}
// Update Backedge branch separately from invocations
const Register G4_invoke_ctr = G4;
__ increment_backedge_counter(G4_invoke_ctr, G1_scratch);
if (ProfileInterpreter) {
__ test_invocation_counter_for_mdp(G4_invoke_ctr, G3_scratch, Lforward);
if (UseOnStackReplacement) {
__ test_backedge_count_for_osr(O2_bumped_count, O0_cur_bcp, G3_scratch);
}
} else {
if (UseOnStackReplacement) {
__ test_backedge_count_for_osr(G4_invoke_ctr, O0_cur_bcp, G3_scratch);
}
}
}
{- -------------------------------------------
(1.1) コード生成:
「(ここが Lforward ラベルの位置)」
---------------------------------------- -}
__ bind(Lforward);
{- -------------------------------------------
(1) コード生成:
UseCompiler または UseLoopCounter のどちらかが指定されていない場合は, 以下のコードを出力するだけ
「Lbcp の値を更新する (O1_disp に入っているオフセット分を足す)」
---------------------------------------- -}
} else
// Bump bytecode pointer by displacement (take the branch)
__ add( O1_disp, Lbcp, Lbcp );// add to bc addr
{- -------------------------------------------
(1) コード生成:
「dispatch_next() が生成するコードで,
次のバイトコードに対応するテンプレートへとジャンプする」
---------------------------------------- -}
// continue with bytecode @ target
// %%%%% Like Intel, could speed things up by moving bytecode fetch to code above,
// %%%%% and changing dispatch_next to dispatch_only
__ dispatch_next(vtos);
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.