hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp
// remove activation
//
// Unlock the receiver if this is a synchronized method.
// Unlock any Java monitors from syncronized blocks.
// Remove the activation from the stack.
//
// If there are locked Java monitors
// If throw_monitor_exception
// throws IllegalMonitorStateException
// Else if install_monitor_exception
// installs IllegalMonitorStateException
// Else
// no error processing
void InterpreterMacroAssembler::remove_activation(
TosState state,
Register ret_addr,
bool throw_monitor_exception,
bool install_monitor_exception,
bool notify_jvmdi) {
{- -------------------------------------------
(1)
---------------------------------------- -}
// Note: Registers rdx xmm0 may be in use for the
// result check if synchronized method
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Label unlocked, unlock, no_unlock;
{- -------------------------------------------
(1) コード生成:
「カレントスレッド内の JavaThread::do_not_unlock_if_synchronized フィールドの値を rdx に取得.
(なお, JavaThread::do_not_unlock_if_synchronized フィールド自体は 0 に戻す)」
(このフィールドの意味については, メソッドエントリ部の処理を参照. (See: [here](no3059n2f.html) for details))
---------------------------------------- -}
// get the value of _do_not_unlock_if_synchronized into rdx
const Address do_not_unlock_if_synchronized(r15_thread,
in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
movbool(rdx, do_not_unlock_if_synchronized);
movbool(do_not_unlock_if_synchronized, false); // reset the flag
{- -------------------------------------------
(1) コード生成:
「呼び出したメソッドが synchronized 指定されているかどうかをチェック.
synchronized でない場合は, することはないので unlocked までジャンプ」
---------------------------------------- -}
// get method access flags
movptr(rbx, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
movl(rcx, Address(rbx, methodOopDesc::access_flags_offset()));
testl(rcx, JVM_ACC_SYNCHRONIZED);
jcc(Assembler::zero, unlocked);
{- -------------------------------------------
(1) コード生成:
「取得してあった JavaThread::do_not_unlock_if_synchronized フィールドの値を確認する.
もし true だった場合には, 何もしなくてよいので no_unlock までジャンプ」
---------------------------------------- -}
// Don't unlock anything if the _do_not_unlock_if_synchronized flag
// is set.
testbool(rdx);
jcc(Assembler::notZero, no_unlock);
{- -------------------------------------------
(1) (以下で, 実際のロック解放処理を行う)
---------------------------------------- -}
// unlock monitor
{- -------------------------------------------
(1) コード生成:
「TOS に入っている返値をスタック上に待避しておく」
---------------------------------------- -}
push(state); // save result
{- -------------------------------------------
(1) (synchronized method なので, 先頭の BasicObjectLock がアンロック対象になるはず.
ただし, モニターが変になっていないことのチェックが必要.
より具体的には, その BasicObjectLock が monitorexit によって既に解放されていないこと, をチェックしておく.)
---------------------------------------- -}
// BasicObjectLock will be first in list, since this is a
// synchronized method. However, need to check that the object has
// not been unlocked by an explicit monitorexit bytecode.
{- -------------------------------------------
(1) コード生成:
「先頭の BasicObjectLock 内の obj フィールドを確認しておく.
NULL でなければ (= 解放済みでなければ), unlock ラベルにジャンプしてアンロック処理を行う.
逆に NULL の場合は (= 既に解放済みの場合は), このままフォールスルーして以下のどれかの処理を行う.
(なお, 以下の処理はコード生成時に引数に応じてどれかが生成される. 動的に条件判定で処理を分岐するわけではない)
* 引数の throw_monitor_exception が true の場合
InterpreterRuntime::throw_illegal_monitor_state_exception() を呼び出して
IllegalMonitorStateException を出す.
* (throw_monitor_exception は false だが) 引数の install_monitor_exception が true の場合
InterpreterRuntime::new_illegal_monitor_state_exception() を呼び出して
IllegalMonitorStateException を出す.
* それ以外の場合
アンロックできたことにして, unlocked ラベルにジャンプする. 」
---------------------------------------- -}
const Address monitor(rbp, frame::interpreter_frame_initial_sp_offset *
wordSize - (int) sizeof(BasicObjectLock));
// We use c_rarg1 so that if we go slow path it will be the correct
// register for unlock_object to pass to VM directly
lea(c_rarg1, monitor); // address of first monitor
movptr(rax, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()));
testptr(rax, rax);
jcc(Assembler::notZero, unlock);
pop(state);
if (throw_monitor_exception) {
// Entry already unlocked, need to throw exception
call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::throw_illegal_monitor_state_exception));
should_not_reach_here();
} else {
// Monitor already unlocked during a stack unroll. If requested,
// install an illegal_monitor_state_exception. Continue with
// stack unrolling.
if (install_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::new_illegal_monitor_state_exception));
}
jmp(unlocked);
}
{- -------------------------------------------
(1) コード生成:
「InterpreterMacroAssembler::unlock_object() が生成するコードで,
ロックの解放処理を行う」
---------------------------------------- -}
bind(unlock);
unlock_object(c_rarg1);
{- -------------------------------------------
(1) コード生成:
「待避していた TOS を復帰させておく」
---------------------------------------- -}
pop(state);
{- -------------------------------------------
(1) コード生成:
「(ここが unlocked ラベルの場所)」
---------------------------------------- -}
// Check that for block-structured locking (i.e., that all locked
// objects has been unlocked)
bind(unlocked);
{- -------------------------------------------
(1) (この段階では, rax に返値が入っている可能性がある.)
---------------------------------------- -}
// rax: Might contain return value
{- -------------------------------------------
(1) (以下で, 全てのモニターがアンロックされていることを確認する.
もしロックが取られたままのモニターがあれば, IllegalMonitorStateException)
(制御フローは少し混み合っていて, 以下のようになっている. 何でこんな書き方になってるのか?? #TODO
処理は (restart ラベルを経由して) entry ラベルから始まり,
その後 loop で全部の要素の処理が終わるまでループが繰り返される.
ただし, ロックが取られたままの要素が見つかれば, exception にジャンプして例外送出が行われる.
ただし, exception で例外送出が行われないケースもあり, その場合は
(restart ラベルを経由して) ループが再開される.)
---------------------------------------- -}
// Check that all monitors are unlocked
{
Label loop, exception, entry, restart;
const int entry_size = frame::interpreter_frame_monitor_size() * wordSize;
const Address monitor_block_top(
rbp, frame::interpreter_frame_monitor_block_top_offset * wordSize);
const Address monitor_block_bot(
rbp, frame::interpreter_frame_initial_sp_offset * wordSize);
{- -------------------------------------------
(1.1) コード生成:
「(ここが restart ラベルの位置)」
---------------------------------------- -}
bind(restart);
{- -------------------------------------------
(1.1) コード生成:
「entry ラベルにジャンプして, 確認処理を開始する」
---------------------------------------- -}
// We use c_rarg1 so that if we go slow path it will be the correct
// register for unlock_object to pass to VM directly
movptr(c_rarg1, monitor_block_top); // points to current entry, starting
// with top-most entry
lea(rbx, monitor_block_bot); // points to word before bottom of
// monitor block
jmp(entry);
{- -------------------------------------------
(1.1) コード生成:
「(ここが exception ラベルの位置)
以下のどれかの処理を行う.
(なお, 以下の処理はコード生成時に引数に応じてどれかが生成される. 動的に条件判定で処理を分岐するわけではない)
* 引数の throw_monitor_exception が true の場合
InterpreterRuntime::throw_illegal_monitor_state_exception() を呼び出して
IllegalMonitorStateException を出す.
* (throw_monitor_exception は false だが) 引数の install_monitor_exception が true の場合
InterpreterRuntime::new_illegal_monitor_state_exception() を呼び出して
IllegalMonitorStateException を出す.
* それ以外の場合
何もしない. このまま restart ラベルにジャンプして次の要素の処理に進む.」
---------------------------------------- -}
// Entry already locked, need to throw exception
bind(exception);
if (throw_monitor_exception) {
// Throw exception
MacroAssembler::call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::
throw_illegal_monitor_state_exception));
should_not_reach_here();
} else {
// Stack unrolling. Unlock object and install illegal_monitor_exception.
// Unlock does not block, so don't have to worry about the frame.
// We don't have to preserve c_rarg1 since we are going to throw an exception.
push(state);
unlock_object(c_rarg1);
pop(state);
if (install_monitor_exception) {
call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::
new_illegal_monitor_state_exception));
}
jmp(restart);
}
{- -------------------------------------------
(1.1) コード生成:
「(ここが loop ラベルの位置)
もし obj フィールドが NULL であれば, exception ラベルにジャンプして例外送出を行う.
そうでなければ, c_rarg1 をインクリメントした後, このままフォールスルー.」
---------------------------------------- -}
bind(loop);
// check if current entry is used
cmpptr(Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()), (int32_t) NULL);
jcc(Assembler::notEqual, exception);
addptr(c_rarg1, entry_size); // otherwise advance to next entry
{- -------------------------------------------
(1.1) コード生成:
「(ここが entry ラベルの位置)
まだ全ての BasicObjectLock を処理し終えていなければ,
loop にジャンプして現在の BasicObjectLock オブジェクトの処理を行う.
逆に全ての BasicObjectLock を処理し終えたら, このままフォールスルーして終了」
---------------------------------------- -}
bind(entry);
cmpptr(c_rarg1, rbx); // check if bottom reached
jcc(Assembler::notEqual, loop); // if not at bottom then check this entry
}
{- -------------------------------------------
(1) (ここまでが, 全てのモニターがアンロックされていることを確認する処理)
---------------------------------------- -}
{- -------------------------------------------
(1) コード生成:
「(ここが no_unlock ラベル)」
---------------------------------------- -}
bind(no_unlock);
{- -------------------------------------------
(1) コード生成: (JVMTI のフック点)
---------------------------------------- -}
// jvmti support
if (notify_jvmdi) {
notify_method_exit(state, NotifyJVMTI); // preserve TOSCA
} else {
notify_method_exit(state, SkipNotifyJVMTI); // preserve TOSCA
}
{- -------------------------------------------
(1) コード生成:
「スタックフレーム上に待避しておいた SP の値を取得する」
---------------------------------------- -}
// remove activation
// get sender sp
movptr(rbx,
Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
{- -------------------------------------------
(1) コード生成:
「スタックフレームを破棄し, リターンアドレスを取得した後,
SP を待避しておいた値に復帰させる」
---------------------------------------- -}
leave(); // remove frame anchor
pop(ret_addr); // get return address
mov(rsp, rbx); // set sp to sender sp
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.