hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
void TemplateInterpreterGenerator::generate_throw_exception() {
{- -------------------------------------------
(1) (ここからが, 例外が rethrow された場合の処理コード.
ここで生成したコードは Interpreter::rethrow_exception_entry() からアクセスされる.)
(とは言っても, 処理自体は例外送出時と同じなので, このまま以下の
Interpreter::_throw_exception_entry の処理にフォールスルーするだけ)
---------------------------------------- -}
// Entry point in previous activation (i.e., if the caller was interpreted)
Interpreter::_rethrow_exception_entry = __ pc();
// O0: exception
{- -------------------------------------------
(1) (ここからが, 例外発生時の例外送出処理コード.
ここで生成したコードは Interpreter::throw_exception_entry() からアクセスされる.)
---------------------------------------- -}
// entry point for exceptions thrown within interpreter code
Interpreter::_throw_exception_entry = __ pc();
{- -------------------------------------------
(1) コード生成: (verify)
---------------------------------------- -}
__ verify_thread();
// expression stack is undefined here
// O0: exception, i.e. Oexception
// Lbcp: exception bcx
__ verify_oop(Oexception);
{- -------------------------------------------
(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 C routine to find handler and jump to it
__ call_VM(O1, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), Oexception);
__ push_ptr(O1); // push exception for exception handler bytecodes
__ JMP(O0, 0); // jump to exception handler (may be remove activation entry!)
__ delayed()->nop();
// 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
// Lbcp: exception bcp
{- -------------------------------------------
(1) (ここまでが, 例外発生時の例外送出処理用のコード)
---------------------------------------- -}
{- -------------------------------------------
(1) (ここからは, JVMTI の PopFrame() 処理用のコード.
ここで生成したコードは Interpreter::remove_activation_preserving_args_entry() からアクセスされる.)
---------------------------------------- -}
//
// JVMTI PopFrame support
//
Interpreter::_remove_activation_preserving_args_entry = __ pc();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
Address popframe_condition_addr(G2_thread, JavaThread::popframe_condition_offset());
{- -------------------------------------------
(1) コード生成:
「これ以降に起こった call_VM でまたこの関数に re-enter してしまわないように
(= PopFrame については既に作業中であることを示すために),
カレントスレッドの JavaThread::_popframe_condition フィールド中の
JavaThread::popframe_processing_bit ビットを立てておく.」
---------------------------------------- -}
// Set the popframe_processing bit in popframe_condition indicating that we are
// currently handling popframe, so that call_VMs that may happen later do not trigger new
// popframe handling cycles.
__ ld(popframe_condition_addr, G3_scratch);
__ or3(G3_scratch, JavaThread::popframe_processing_bit, G3_scratch);
__ stw(G3_scratch, popframe_condition_addr);
{- -------------------------------------------
(1) コード生成:
「オペランドスタック("expression stack")を空にする.」
(これは通常の例外処理等と同じ)
---------------------------------------- -}
// Empty the expression stack, as in normal exception handling
__ empty_expression_stack();
{- -------------------------------------------
(1) コード生成:
「InterpreterMacroAssembler::unlock_if_synchronized_method() が生成するコードで,
(もし synchronized メソッドの場合には) ロックを解放する.
モニターのロック状態がおかしければ, IllegalMonitorStateException も送出される」
---------------------------------------- -}
__ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, /* install_monitor_exception */ false);
{- -------------------------------------------
(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;
__ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), I7);
__ tst(O0);
__ brx(Assembler::notEqual, false, Assembler::pt, caller_not_deoptimized);
__ delayed()->nop();
{- -------------------------------------------
(1) コード生成:
「(以下が, 呼び出し元が compiled frame の場合の処理)
この場合は, PopFrame() 対象のフレームが破棄された後,
呼び出し元に対する deopt 処理が行われ, それが終わった後で invoke 命令が再実行される.
(引数については deopt 処理中に再セットが行われ, deopt 終了後には
bci が invoke 命令を指した状態で interpreter に引き継がれる.
See: vframeArrayElement::unpack_on_stack()) 」
---------------------------------------- -}
const Register Gtmp1 = G3_scratch;
const Register Gtmp2 = G1_scratch;
{- -------------------------------------------
(1.1) コード生成:
「invoke を再実行するために必要な引数の情報を
Deoptimization::popframe_preserve_args() で退避する.」
---------------------------------------- -}
// Compute size of arguments for saving when returning to deoptimized caller
__ lduh(Lmethod, in_bytes(methodOopDesc::size_of_parameters_offset()), Gtmp1);
__ sll(Gtmp1, Interpreter::logStackElementSize, Gtmp1);
__ sub(Llocals, Gtmp1, Gtmp2);
__ add(Gtmp2, wordSize, Gtmp2);
// Save these arguments
__ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), G2_thread, Gtmp1, Gtmp2);
{- -------------------------------------------
(1.1) コード生成:
「deopt 処理中で, 今回の処理対象が「PopFrame() 対象の呼び出し元」であることが把握できるよう,
JavaThread::_popframe_condition フィールドの
JavaThread::popframe_force_deopt_reexecution_bit ビットが立てておく.」
---------------------------------------- -}
// Inform deoptimization that it is responsible for restoring these arguments
__ set(JavaThread::popframe_force_deopt_reexecution_bit, Gtmp1);
Address popframe_condition_addr(G2_thread, JavaThread::popframe_condition_offset());
__ st(Gtmp1, popframe_condition_addr);
{- -------------------------------------------
(1.1) コード生成:
「restore 命令でレジスタを復帰(SP も I5_savedSP に復帰)させつつ, リターンする.
(restore で PopFrame() 対象になったフレームを破棄し,
リターンすることで呼び出し元に対する deopt 処理を開始させる)」
---------------------------------------- -}
// Return from the current method
// The caller's SP was adjusted upon method entry to accomodate
// the callee's non-argument locals. Undo that adjustment.
__ ret();
__ delayed()->restore(I5_savedSP, G0, SP);
{- -------------------------------------------
(1) コード生成:
「(以下が, 呼び出し元が interpreter frame の場合の処理)」
---------------------------------------- -}
__ bind(caller_not_deoptimized);
}
{- -------------------------------------------
(1.1) コード生成:
「カレントスレッドの JavaThread::_popframe_condition フィールドをリセットする.」
---------------------------------------- -}
// Clear the popframe condition flag
__ stw(G0 /* popframe_inactive */, popframe_condition_addr);
{- -------------------------------------------
(1.1) コード生成:
「restore 命令で PopFrame() 対象のフレームを破棄し
(ついでにレジスタを復帰させ, SP も I5_savedSP に復帰させ),
dispatch_next() で再度 invoke 命令を実行させる.
(なお, ProfileInterpreter オプションが指定されていれば,
既にインクリメントされてしまっている method data pointer を元に戻しておく) 」
---------------------------------------- -}
// Get out of the current method (how this is done depends on the particular compiler calling
// convention that the interpreter currently follows)
// The caller's SP was adjusted upon method entry to accomodate
// the callee's non-argument locals. Undo that adjustment.
__ restore(I5_savedSP, G0, SP);
// 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();
}
// Resume bytecode interpretation at the current bcp
__ dispatch_next(vtos);
{- -------------------------------------------
(1) (ここまでが, JVMTI の PopFrame() 処理用のコード)
---------------------------------------- -}
// end of JVMTI PopFrame support
{- -------------------------------------------
(1) (ここからは, 例外発生時の unwind 処理用のコード.
ここで生成したコードは Interpreter::remove_activation_entry() からアクセスされる.)
---------------------------------------- -}
Interpreter::_remove_activation_entry = __ pc();
{- -------------------------------------------
(1) コード生成:
「送出された例外オブジェクトを Oexception レジスタに入れておく」
---------------------------------------- -}
// preserve exception over this code sequence (remove activation calls the vm, but oopmaps are not correct here)
__ pop_ptr(Oexception); // get exception
{- -------------------------------------------
(1) コード生成:
「InterpreterMacroAssembler::unlock_if_synchronized_method() が生成するコードで,
(もし synchronized メソッドの場合には) ロックを解放する.
モニターのロック状態がおかしければ, IllegalMonitorStateException も送出される.
(なお処理の前後で, set_vm_result()/get_vm_result() により
送出された例外オブジェクトの退避復帰も行っている) 」
---------------------------------------- -}
// Intel has the following comment:
//// remove the activation (without doing throws on illegalMonitorExceptions)
// They remove the activation without checking for bad monitor state.
// %%% We should make sure this is the right semantics before implementing.
// %%% changed set_vm_result_2 to set_vm_result and get_vm_result_2 to get_vm_result. Is there a bug here?
__ set_vm_result(Oexception);
__ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false);
{- -------------------------------------------
(1) コード生成: (JVMTI のフック点) (DTrace のフック点)
---------------------------------------- -}
__ notify_method_exit(false, vtos, InterpreterMacroAssembler::SkipNotifyJVMTI);
__ get_vm_result(Oexception);
{- -------------------------------------------
(1) コード生成: (verify)
---------------------------------------- -}
__ verify_oop(Oexception);
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
const int return_reg_adjustment = frame::pc_return_offset;
Address issuing_pc_addr(I7, return_reg_adjustment);
{- -------------------------------------------
(1) コード生成:
「SharedRuntime::exception_handler_for_return_address() を呼んで,
呼び出し元のメソッドに応じた unwind 先アドレスを取得する.
(なお, レジスタについては, 以下のようにしている.
* I* レジスタ, L* レジスタ
値を変更しないようにしている ("caller's register window" のまま)
* Oexception, Oissuing_pc
unwind 先の例外ハンドラが必要とするため, 値をセットしている.
なお, unwind 先には restore 後にジャンプするので,
セット対象のレジスタは restore を考慮して after_save() で取得している.) 」
---------------------------------------- -}
// We are done with this activation frame; find out where to go next.
// The continuation point will be an exception handler, which expects
// the following registers set up:
//
// Oexception: exception
// Oissuing_pc: the local call that threw exception
// Other On: garbage
// In/Ln: the contents of the caller's register window
//
// We do the required restore at the last possible moment, because we
// need to preserve some state across a runtime call.
// (Remember that the caller activation is unknown--it might not be
// interpreted, so things like Lscratch are useless in the caller.)
// Although the Intel version uses call_C, we can use the more
// compact call_VM. (The only real difference on SPARC is a
// harmlessly ignored [re]set_last_Java_frame, compared with
// the Intel code which lacks this.)
__ mov(Oexception, Oexception ->after_save()); // get exception in I0 so it will be on O0 after restore
__ add(issuing_pc_addr, Oissuing_pc->after_save()); // likewise set I1 to a value local to the caller
__ super_call_VM_leaf(L7_thread_cache,
CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address),
G2_thread, Oissuing_pc->after_save());
{- -------------------------------------------
(1) コード生成:
「restore で現在のフレームが破棄しつつ,
(ついでにレジスタを復帰させ, SP も I5_savedSP に復帰させつつ)
取得した unwind 先にジャンプする」
---------------------------------------- -}
// The caller's SP was adjusted upon method entry to accomodate
// the callee's non-argument locals. Undo that adjustment.
__ JMP(O0, 0); // return exception handler in caller
__ delayed()->restore(I5_savedSP, G0, SP);
// (same old exception object is already in Oexception; see above)
// Note that an "issuing PC" is actually the next PC after the call
{- -------------------------------------------
(1) (ここまでが, 例外発生時の unwind 処理用のコード)
---------------------------------------- -}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.