hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
void TemplateInterpreterGenerator::generate_throw_exception() {
{- -------------------------------------------
(1) (ここからが, 例外が rethrow された場合の処理コード.
ここで生成したコードは Interpreter::rethrow_exception_entry() からアクセスされる.)
(とは言っても, 処理自体は例外送出時とほとんど同じなので,
sp や bcp, locals, r12(heapbase) の復帰を行った後,
このまま以下の Interpreter::_throw_exception_entry の処理にフォールスルーするだけ)
---------------------------------------- -}
// Entry point in previous activation (i.e., if the caller was
// interpreted)
Interpreter::_rethrow_exception_entry = __ pc();
// Restore sp to interpreter_frame_last_sp even though we are going
// to empty the expression stack for the exception processing.
__ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);
// rax: exception
// rdx: return address/pc that threw exception
__ restore_bcp(); // r13 points to call/send
__ restore_locals();
__ reinit_heapbase(); // restore r12 as heapbase.
{- -------------------------------------------
(1) (ここからが, 例外発生時の例外送出処理コード.
ここで生成したコードは Interpreter::throw_exception_entry() からアクセスされる.)
---------------------------------------- -}
// Entry point for exceptions thrown within interpreter code
Interpreter::_throw_exception_entry = __ pc();
// expression stack is undefined here
// rax: exception
// r13: exception bcp
{- -------------------------------------------
(1) コード生成: (verify)
---------------------------------------- -}
__ verify_oop(rax);
{- -------------------------------------------
(1) コード生成:
「?? 創出された例外オブジェクトが
以下で呼び出す InterpreterRuntime::exception_handler_for_exception() から見えるように
c_rarg1 にコピーしている?? #TODO」
---------------------------------------- -}
__ mov(c_rarg1, rax);
{- -------------------------------------------
(1) コード生成:
「オペランドスタック("expression stack")を空にする.」
---------------------------------------- -}
// expression stack must be empty before entering the VM in case of
// an exception
__ empty_expression_stack();
{- -------------------------------------------
(1) コード生成:
「InterpreterRuntime::exception_handler_for_exception() を呼んで
対応する例外ハンドラのエントリポイント(対応するものがなければ unwind 処理のエントリポイント)を取得し,
そのアドレスにジャンプする.
(なお, 例外ハンドラ内で例外オブジェクトにアクセスできるよう
ジャンプ前に push_ptr() で例外オブジェクトをオペランドスタックに積んでいる)」
---------------------------------------- -}
// find exception handler address and preserve exception oop
__ call_VM(rdx,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::exception_handler_for_exception),
c_rarg1);
// rax: exception handler entry point
// rdx: preserved exception oop
// r13: bcp for exception handler
__ push_ptr(rdx); // push exception which is now the only value on the stack
__ jmp(rax); // jump to exception handler (may be _remove_activation_entry!)
// If the exception is not handled in the current frame the frame is
// removed and the exception is rethrown (i.e. exception
// continuation is _rethrow_exception).
//
// Note: At this point the bci is still the bxi for the instruction
// which caused the exception and the expression stack is
// empty. Thus, for any VM calls at this point, GC will find a legal
// oop map (with empty expression stack).
// In current activation
// tos: exception
// esi: exception bcp
{- -------------------------------------------
(1) (ここまでが, 例外発生時の例外送出処理用のコード)
---------------------------------------- -}
{- -------------------------------------------
(1) (ここからは, JVMTI の PopFrame() 処理用のコード.
ここで生成したコードは Interpreter::remove_activation_preserving_args_entry() からアクセスされる.)
---------------------------------------- -}
//
// JVMTI PopFrame support
//
Interpreter::_remove_activation_preserving_args_entry = __ pc();
{- -------------------------------------------
(1) コード生成:
「オペランドスタック("expression stack")を空にする.」
---------------------------------------- -}
__ empty_expression_stack();
{- -------------------------------------------
(1) コード生成:
「これ以降に起こった call_VM でまたこの関数に re-enter してしまわないように
(= PopFrame については既に作業中であることを示すために),
カレントスレッドの JavaThread::_popframe_condition フィールド中の
JavaThread::popframe_processing_bit ビットを立てておく.」
---------------------------------------- -}
// Set the popframe_processing bit in pending_popframe_condition
// indicating that we are currently handling popframe, so that
// call_VMs that may happen later do not trigger new popframe
// handling cycles.
__ movl(rdx, Address(r15_thread, JavaThread::popframe_condition_offset()));
__ orl(rdx, JavaThread::popframe_processing_bit);
__ movl(Address(r15_thread, JavaThread::popframe_condition_offset()), rdx);
{- -------------------------------------------
(1) コード生成:
「InterpreterRuntime::interpreter_contains() を呼んで
PopFrame() 対象になっているフレームの呼び出し元を確認し, その種類に応じて処理を分岐.
* 呼び出し元が compiled frame の場合:
このままフォールスルー
* 呼び出し元が interpreter frame の場合:
caller_not_deoptimized ラベルまでジャンプ 」
---------------------------------------- -}
{
// Check to see whether we are returning to a deoptimized frame.
// (The PopFrame call ensures that the caller of the popped frame is
// either interpreted or compiled and deoptimizes it if compiled.)
// In this case, we can't call dispatch_next() after the frame is
// popped, but instead must save the incoming arguments and restore
// them after deoptimization has occurred.
//
// Note that we don't compare the return PC against the
// deoptimization blob's unpack entry because of the presence of
// adapter frames in C2.
Label caller_not_deoptimized;
__ movptr(c_rarg1, Address(rbp, frame::return_addr_offset * wordSize));
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
InterpreterRuntime::interpreter_contains), c_rarg1);
__ testl(rax, rax);
__ jcc(Assembler::notZero, caller_not_deoptimized);
{- -------------------------------------------
(1) コード生成:
「(以下が, 呼び出し元が compiled frame の場合の処理)
この場合は, PopFrame() 対象のフレームが破棄された後,
呼び出し元に対する deopt 処理が行われ, それが終わった後で invoke 命令が再実行される.
(引数については deopt 処理中に再セットが行われ, deopt 終了後には
bci が invoke 命令を指した状態で interpreter に引き継がれる.
See: vframeArrayElement::unpack_on_stack()) 」
---------------------------------------- -}
{- -------------------------------------------
(1.1) コード生成:
「invoke を再実行するために必要な引数の情報を
Deoptimization::popframe_preserve_args() で退避する.」
---------------------------------------- -}
// Compute size of arguments for saving when returning to
// deoptimized caller
__ get_method(rax);
__ load_unsigned_short(rax, Address(rax, in_bytes(methodOopDesc::
size_of_parameters_offset())));
__ shll(rax, Interpreter::logStackElementSize);
__ restore_locals(); // XXX do we need this?
__ subptr(r14, rax);
__ addptr(r14, wordSize);
// Save these arguments
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
Deoptimization::
popframe_preserve_args),
r15_thread, rax, r14);
{- -------------------------------------------
(1.1) コード生成:
「フレームを破棄し synchronized method であればロックの解除を行う.」
(See: InterpreterMacroAssembler::remove_activation())
---------------------------------------- -}
__ remove_activation(vtos, rdx,
/* throw_monitor_exception */ false,
/* install_monitor_exception */ false,
/* notify_jvmdi */ false);
{- -------------------------------------------
(1.1) コード生成:
「deopt 処理中で, 今回の処理対象が「PopFrame() 対象の呼び出し元」であることが把握できるよう,
JavaThread::_popframe_condition フィールドの
JavaThread::popframe_force_deopt_reexecution_bit ビットが立てておく.」
---------------------------------------- -}
// Inform deoptimization that it is responsible for restoring
// these arguments
__ movl(Address(r15_thread, JavaThread::popframe_condition_offset()),
JavaThread::popframe_force_deopt_reexecution_bit);
{- -------------------------------------------
(1.1) コード生成:
「deopt 処理コードにジャンプ」
---------------------------------------- -}
// Continue in deoptimization handler
__ jmp(rdx);
{- -------------------------------------------
(1) コード生成:
「(以下が, 呼び出し元が interpreter frame の場合の処理)」
---------------------------------------- -}
__ bind(caller_not_deoptimized);
}
{- -------------------------------------------
(1.1) コード生成:
「フレームを破棄し, もし synchronized method であればロックの解除も行う.
(See: InterpreterMacroAssembler::remove_activation()) 」
---------------------------------------- -}
__ remove_activation(vtos, rdx, /* rdx result (retaddr) is not used */
/* throw_monitor_exception */ false,
/* install_monitor_exception */ false,
/* notify_jvmdi */ false);
{- -------------------------------------------
(1.1) コード生成:
「InterpreterRuntime::popframe_move_outgoing_args() を呼んで
invoke の再実行に必要な引数を正しい位置に詰め直しておく.
(i2c によって outgoing argument が移動させられている可能性があるが,
PopFrame() の仕様として再度 invoke が実行された際には
現在の(= 何か変更が行われていればその変更が反映された)引数が見えなければならず,
かつ(他の制約上の都合から) deopt 後の interpreter には
現在の last_sp の値(オペランドスタック("expression stack")の先頭)と
実際の stack top が同じであるように見えないといけない.)
(なお, 呼び出しの前後で last_Java_sp への SP のセット/クリアも行っている)
(また, 呼び出し後には rsp を last_sp まで復帰させ, 代わりに last_sp の値は 0 でクリアしている) 」
---------------------------------------- -}
// Finish with popframe handling
// A previous I2C followed by a deoptimization might have moved the
// outgoing arguments further up the stack. PopFrame expects the
// mutations to those outgoing arguments to be preserved and other
// constraints basically require this frame to look exactly as
// though it had previously invoked an interpreted activation with
// no space between the top of the expression stack (current
// last_sp) and the top of stack. Rather than force deopt to
// maintain this kind of invariant all the time we call a small
// fixup routine to move the mutated arguments onto the top of our
// expression stack if necessary.
__ mov(c_rarg1, rsp);
__ movptr(c_rarg2, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
// PC must point into interpreter here
__ set_last_Java_frame(noreg, rbp, __ pc());
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), r15_thread, c_rarg1, c_rarg2);
__ reset_last_Java_frame(true, true);
// Restore the last_sp and null it out
__ movptr(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
__ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);
{- -------------------------------------------
(1.1) コード生成:
「?? #TODO」
---------------------------------------- -}
__ restore_bcp(); // XXX do we need this?
__ restore_locals(); // XXX do we need this?
{- -------------------------------------------
(1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には行わない)
「既にインクリメントされてしまっている method data pointer を元に戻しておく」
---------------------------------------- -}
// The method data pointer was incremented already during
// call profiling. We have to restore the mdp for the current bcp.
if (ProfileInterpreter) {
__ set_method_data_pointer_for_bcp();
}
{- -------------------------------------------
(1.1) コード生成:
「カレントスレッドの JavaThread::_popframe_condition フィールドをリセットする.」
---------------------------------------- -}
// Clear the popframe condition flag
__ movl(Address(r15_thread, JavaThread::popframe_condition_offset()),
JavaThread::popframe_inactive);
{- -------------------------------------------
(1.1) コード生成:
「dispatch_next() で再度 invoke 命令を実行させる」
---------------------------------------- -}
__ dispatch_next(vtos);
{- -------------------------------------------
(1) (ここまでが, JVMTI の PopFrame() 処理用のコード)
---------------------------------------- -}
// end of PopFrame support
{- -------------------------------------------
(1) (ここからは, 例外発生時の unwind 処理用のコード.
ここで生成したコードは Interpreter::remove_activation_entry() からアクセスされる.)
---------------------------------------- -}
Interpreter::_remove_activation_entry = __ pc();
{- -------------------------------------------
(1) コード生成:
「送出された例外オブジェクトを rax レジスタに入れておく」
---------------------------------------- -}
// preserve exception over this code sequence
__ pop_ptr(rax);
__ movptr(Address(r15_thread, JavaThread::vm_result_offset()), rax);
{- -------------------------------------------
(1) コード生成:
「フレームを破棄し, もし synchronized method であればロックの解除も行う.
(また, (JVMTI のフック点)および(DTrace のフック点)でもある)
(See: InterpreterMacroAssembler::remove_activation())
(なお処理の前後で, set_vm_result()/get_vm_result() により
送出された例外オブジェクトの退避復帰も行っている) 」
---------------------------------------- -}
// remove the activation (without doing throws on illegalMonitorExceptions)
__ remove_activation(vtos, rdx, false, true, false);
// restore exception
__ movptr(rax, Address(r15_thread, JavaThread::vm_result_offset()));
__ movptr(Address(r15_thread, JavaThread::vm_result_offset()), (int32_t)NULL_WORD);
{- -------------------------------------------
(1) コード生成: (verify)
---------------------------------------- -}
__ verify_oop(rax);
{- -------------------------------------------
(1) コード生成:
「SharedRuntime::exception_handler_for_return_address() を呼んで,
呼び出し元のメソッドに応じた unwind 先アドレスを取得する.
(なお, 呼び出しの前後で rax(送出された例外) と rdx(リターンアドレス) の退避復帰を行っている)
(また, unwind 先の例外ハンドラでは
以下のレジスタがセットアップされていると仮定している模様.
* rax: exception
* rdx: return address/pc that threw exception
* rsp: expression stack of caller
* rbp: ebp of caller ) 」
---------------------------------------- -}
// In between activations - previous activation type unknown yet
// compute continuation point - the continuation point expects the
// following registers set up:
//
// rax: exception
// rdx: return address/pc that threw exception
// rsp: expression stack of caller
// rbp: ebp of caller
__ push(rax); // save exception
__ push(rdx); // save return address
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
SharedRuntime::exception_handler_for_return_address),
r15_thread, rdx);
__ mov(rbx, rax); // save exception handler
__ pop(rdx); // restore return address
__ pop(rax); // restore exception
{- -------------------------------------------
(1) コード生成:
「取得した unwind 先にジャンプする.」
---------------------------------------- -}
// Note that an "issuing PC" is actually the next PC after the call
__ jmp(rbx); // jump to exception
// handler of caller
{- -------------------------------------------
(1) (ここまでが, 例外発生時の unwind 処理用のコード)
---------------------------------------- -}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.