hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp
void TemplateTable::branch(bool is_jsr, bool is_wide) {
{- -------------------------------------------
(1)
---------------------------------------- -}
__ get_method(rcx); // rcx holds method
{- -------------------------------------------
(1) コード生成: (JIT コンパイラ用のプロファイル情報の取得) (See: [here](no2935fdD.html) for details)
---------------------------------------- -}
__ profile_taken_branch(rax, rbx); // rax holds updated MDP, rbx
// holds bumped taken count
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
const ByteSize be_offset = methodOopDesc::backedge_counter_offset() +
InvocationCounter::counter_offset();
const ByteSize inv_offset = methodOopDesc::invocation_counter_offset() +
InvocationCounter::counter_offset();
const int method_offset = frame::interpreter_frame_method_offset * wordSize;
{- -------------------------------------------
(1) コード生成:
「飛び先のオフセットを取得する」
---------------------------------------- -}
// Load up edx with the branch displacement
__ movl(rdx, at_bcp(1));
__ bswapl(rdx);
if (!is_wide) {
__ sarl(rdx, 16);
}
__ movl2ptr(rdx, rdx);
{- -------------------------------------------
(1) コード生成: (jsr 用のコードを生成する場合)
jsr 用の場合は, 以下のようなコードを出力し, ここでリターン.
「rbx に次のバイトコードのセットする.
また, リターンアドレスとして現在値を表す bci 値を算出し, オペランドスタックの先頭に乗せる.
さらに r13 を飛び先の値に変更する.
その後, dispatch_only() が生成するコードで,
次のバイトコードに対応するテンプレートへとジャンプする」
---------------------------------------- -}
// 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) {
// Pre-load the next target bytecode into rbx
__ load_unsigned_byte(rbx, Address(r13, rdx, Address::times_1, 0));
// compute return address as bci in rax
__ lea(rax, at_bcp((is_wide ? 5 : 3) -
in_bytes(constMethodOopDesc::codes_offset())));
__ subptr(rax, Address(rcx, methodOopDesc::const_offset()));
// Adjust the bcp in r13 by the displacement in rdx
__ addptr(r13, rdx);
// jsr returns atos that is not an oop
__ push_i(rax);
__ dispatch_only(vtos);
return;
}
{- -------------------------------------------
(1) (以下は, jsr ではない場合のコード生成)
---------------------------------------- -}
// Normal (non-jsr) branch handling
{- -------------------------------------------
(1) コード生成:
「r13 の値を更新しておく (rdx に入っているジャンプ先のオフセット分を足す)」
---------------------------------------- -}
// Adjust the bcp in r13 by the displacement in rdx
__ addptr(r13, rdx);
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(UseLoopCounter || !UseOnStackReplacement,
"on-stack-replacement requires loop counters");
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Label backedge_counter_overflow;
Label profile_method;
Label dispatch;
{- -------------------------------------------
(1) もし UseLoopCounter が指定されていれば,
以下の処理パスで backward branch 用の処理を行うコードを出力する.
---------------------------------------- -}
if (UseLoopCounter) {
{- -------------------------------------------
(1.1) (この時点では, 各レジスタには以下の値が入っているはず)
---------------------------------------- -}
// increment backedge counter for backward branches
// rax: MDO
// ebx: MDO bumped taken-count
// rcx: method
// rdx: target offset
// r13: target bcp
// r14: locals pointer
{- -------------------------------------------
(1.1) コード生成:
「分岐の向きをチェックして,
forward branch であれば (backward branch 用の処理は不要なので) Lforward まで進む.」
---------------------------------------- -}
__ testl(rdx, rdx); // check if forward or backward branch
__ jcc(Assembler::positive, dispatch); // count only if backward branch
{- -------------------------------------------
(1.1) TieredCompilation が指定されている場合, 以下の処理パスでコードを生成する
---------------------------------------- -}
if (TieredCompilation) {
{- -------------------------------------------
(1.1.1) (変数宣言など)
---------------------------------------- -}
Label no_mdo;
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 フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
閾値に達していた場合は, backedge_counter_overflow ラベルにジャンプする.
(= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).
そうでなければ, dispatch ラベルまでジャンプする.
もし methodDataOopDesc が付いていなければ,
no_mdo ラベルまでジャンプする」
---------------------------------------- -}
if (ProfileInterpreter) {
// Are we profiling?
__ movptr(rbx, Address(rcx, in_bytes(methodOopDesc::method_data_offset())));
__ testptr(rbx, rbx);
__ jccb(Assembler::zero, no_mdo);
// Increment the MDO backedge counter
const Address mdo_backedge_counter(rbx, in_bytes(methodDataOopDesc::backedge_counter_offset()) +
in_bytes(InvocationCounter::counter_offset()));
__ increment_mask_and_jump(mdo_backedge_counter, increment, mask,
rax, false, Assembler::zero, &backedge_counter_overflow);
__ jmp(dispatch);
}
{- -------------------------------------------
(1.1.1) コード生成:
「(ここが no_mdo ラベルの位置)」
---------------------------------------- -}
__ bind(no_mdo);
{- -------------------------------------------
(1.1.1) コード生成:
「InterpreterMacroAssembler::increment_mask_and_jump() が生成するコードによって
実行中のメソッドに対応する methodOopDesc の
_backedge_counter フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
閾値に達していた場合は, backedge_counter_overflow ラベルにジャンプする.
(= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).
そうでなければ, dispatch ラベルまでジャンプする.」
---------------------------------------- -}
// Increment backedge counter in methodOop
__ increment_mask_and_jump(Address(rcx, be_offset), increment, mask,
rax, false, Assembler::zero, &backedge_counter_overflow);
{- -------------------------------------------
(1.1) TieredCompilation が指定されていない場合, 以下の処理パスでコードを生成する
---------------------------------------- -}
} else {
{- -------------------------------------------
(1.1.1) コード生成: (TieredCompilation が指定されていない場合)
まず, 以下のコードを生成する.
「backedge_counter のカウント値をインクリメントする.」
その後, ProfileInterpreter が指定されていれば, 以下のコードを生成する.
「methodDataOop(mdp) に関するチェックを行う.
チェック結果に応じて, 以下の3通りに分岐.
* まだ監視対象になる閾値を超えていない場合:
dispatch ラベルまで分岐.
* 監視対象になる閾値を超えているが, まだ mdp が確保されていない場合
(InterpreterMacroAssembler::test_method_data_pointer() が生成するコードでチェック):
profile_method ラベルまで分岐 (新しい methodDataOop オブジェクトを methodOop 内にセットする)
* 既に methodDataOop が作成済みの場合:
このままフォールスルー
(UseOnStackReplacement が指定されていれば, その if 中で生成するコードに突入)
その後, UseOnStackReplacement が指定されていれば, 以下のコードを生成する.
「InvocationCounter::InterpreterBackwardBranchLimit と現在の値を比較し,
越えてなければ dispatch ラベルまで飛ぶ.
逆に超えていれば, backedge_counter_overflow ラベルまで飛ぶ
(= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).
ただし, ProfileInterpreter オプションが指定されている場合は
frequency_counter_overflow() がカウンタの値をクリアしないので,
一度しきい値を越えると(既に JIT compiler が作業中なのに)連続してコンパイル要求を出すことになりかねない.
そのため, この場合は overflow_frequency という間隔(今は1024)につき1度だけ backedge_counter_overflow に飛ぶよう調整している.
(それ以外の1023回は, 何もせず dispatch ラベルに飛ぶだけにする)」
---------------------------------------- -}
// increment counter
__ movl(rax, Address(rcx, be_offset)); // load backedge counter
__ incrementl(rax, InvocationCounter::count_increment); // increment counter
__ movl(Address(rcx, be_offset), rax); // store counter
__ movl(rax, Address(rcx, inv_offset)); // load invocation counter
__ andl(rax, InvocationCounter::count_mask_value); // and the status bits
__ addl(rax, Address(rcx, be_offset)); // add both counters
if (ProfileInterpreter) {
// Test to see if we should create a method data oop
__ cmp32(rax,
ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit));
__ jcc(Assembler::less, dispatch);
// if no method data exists, go to profile method
__ test_method_data_pointer(rax, profile_method);
if (UseOnStackReplacement) {
// check for overflow against ebx which is the MDO taken count
__ cmp32(rbx,
ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit));
__ jcc(Assembler::below, dispatch);
// When ProfileInterpreter is on, the backedge_count comes
// from the methodDataOop, which value does not get reset on
// the call to frequency_counter_overflow(). To avoid
// excessive calls to the overflow routine while the method is
// being compiled, add a second test to make sure the overflow
// function is called only once every overflow_frequency.
const int overflow_frequency = 1024;
__ andl(rbx, overflow_frequency - 1);
__ jcc(Assembler::zero, backedge_counter_overflow);
}
} else {
if (UseOnStackReplacement) {
// check for overflow against eax, which is the sum of the
// counters
__ cmp32(rax,
ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit));
__ jcc(Assembler::aboveEqual, backedge_counter_overflow);
}
}
}
{- -------------------------------------------
(1) コード生成:
「(ここが dispatch ラベルの位置)」
---------------------------------------- -}
__ bind(dispatch);
}
{- -------------------------------------------
(1) コード生成:
「rbx に次のバイトコードのセットし,
その後, dispatch_only() が生成するコードで,
次のバイトコードに対応するテンプレートへとジャンプする」
---------------------------------------- -}
// Pre-load the next target bytecode into rbx
__ load_unsigned_byte(rbx, Address(r13, 0));
// continue with the bytecode @ target
// eax: return bci for jsr's, unused otherwise
// ebx: target bytecode
// r13: target bcp
__ dispatch_only(vtos);
{- -------------------------------------------
(1) もし UseLoopCounter が指定されていれば,
以下の処理パスで backward branch 用の処理を行うコードを出力する.
---------------------------------------- -}
if (UseLoopCounter) {
{- -------------------------------------------
(1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には生成しない)
「(ここが profile_method ラベルの位置)」
「InterpreterRuntime::profile_method() を呼び出して,
新しい methodDataOop オブジェクトを methodOop 内にセットする.
その後, dispatch ラベルに戻って実行を再開する.」
---------------------------------------- -}
if (ProfileInterpreter) {
// Out-of-line code to allocate method data oop.
__ bind(profile_method);
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method));
__ load_unsigned_byte(rbx, Address(r13, 0)); // restore target bytecode
__ set_method_data_pointer_for_bcp();
__ jmp(dispatch);
}
{- -------------------------------------------
(1.1) もし UseOnStackReplacement が指定されていれば,
以下の処理パスで backward branch 用の処理を行うコードを出力する.
---------------------------------------- -}
if (UseOnStackReplacement) {
{- -------------------------------------------
(1.1.1) コード生成:
「(ここが backedge_counter_overflow ラベルの位置)」
---------------------------------------- -}
// invocation counter overflow
__ bind(backedge_counter_overflow);
{- -------------------------------------------
(1.1.1) コード生成:
「InterpreterRuntime::frequency_counter_overflow() を呼び出して JIT コンパイル処理を開始する.」
---------------------------------------- -}
__ negptr(rdx);
__ addptr(rdx, r13); // branch bcp
// IcoResult frequency_counter_overflow([JavaThread*], address branch_bcp)
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::frequency_counter_overflow),
rdx);
__ load_unsigned_byte(rbx, Address(r13, 0)); // restore target bytecode
{- -------------------------------------------
(1.1.1) コード生成:
「もし InterpreterRuntime::frequency_counter_overflow() の返り値が NULL だったら,
(まだ JIT 生成されたコードがないということなので) dispatch ラベルまで飛ぶ.
また, nmethod が既に invalidate されていた場合も, dispatch ラベルまで飛ぶ.」
---------------------------------------- -}
// rax: osr nmethod (osr ok) or NULL (osr not possible)
// ebx: target bytecode
// rdx: scratch
// r14: locals pointer
// r13: bcp
__ testptr(rax, rax); // test result
__ jcc(Assembler::zero, dispatch); // no osr if null
// nmethod may have been invalidated (VM may block upon call_VM return)
__ movl(rcx, Address(rax, nmethod::entry_bci_offset()));
__ cmpl(rcx, InvalidOSREntryBci);
__ jcc(Assembler::equal, dispatch);
{- -------------------------------------------
(1.1.1) (ここまで来たら, JIT 生成された nmethod が得られたということになる)
---------------------------------------- -}
// We have the address of an on stack replacement routine in eax
// We need to prepare to execute the OSR method. First we must
// migrate the locals and monitors off of the stack.
{- -------------------------------------------
(1.1.1) コード生成:
「取得した mmethod を r13 に待避しておく」
---------------------------------------- -}
__ mov(r13, rax); // save the nmethod
{- -------------------------------------------
(1.1.1) コード生成:
「SharedRuntime::OSR_migration_begin() を呼んで,
interpreter frame 中の局所変数とモニタを C ヒープ上に取ったバッファに退避する.」
---------------------------------------- -}
call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin));
{- -------------------------------------------
(1.1.1) コード生成:
「SharedRuntime::OSR_migration_begin() が返したバッファを j_rarg0 に退避しておく」
---------------------------------------- -}
// eax is OSR buffer, move it to expected parameter location
__ mov(j_rarg0, rax);
{- -------------------------------------------
(1.1.1) (変数宣言など)
---------------------------------------- -}
// We use j_rarg definitions here so that registers don't conflict as parameter
// registers change across platforms as we are in the midst of a calling
// sequence to the OSR nmethod and we don't want collision. These are NOT parameters.
const Register retaddr = j_rarg2;
const Register sender_sp = j_rarg1;
{- -------------------------------------------
(1.1.1) コード生成:
「interpreter frame を削除する.
ついでに, rsp の値が align しているよう調整もしておく.」
---------------------------------------- -}
// pop the interpreter frame
__ movptr(sender_sp, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp
__ leave(); // remove frame anchor
__ pop(retaddr); // get return address
__ mov(rsp, sender_sp); // set sp to sender sp
// Ensure compiled code always sees stack at proper alignment
__ andptr(rsp, -(StackAlignmentInBytes));
{- -------------------------------------------
(1.1.1) コード生成:
「リターンアドレスをプッシュした後, osr コードにジャンプする.」
---------------------------------------- -}
// unlike x86 we need no specialized return from compiled code
// to the interpreter or the call stub.
// push the return address
__ push(retaddr);
// and begin the OSR nmethod
__ jmp(Address(r13, nmethod::osr_entry_point_offset()));
}
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.