hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
// Interpreter stub for calling a native method. (asm interpreter)
// This sets up a somewhat different looking stack for calling the
// native method than the typical interpreter frame setup.
address InterpreterGenerator::generate_native_entry(bool synchronized) {
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
// determine code generation flags
bool inc_counter = UseCompiler || CountCompiledCalls;
{- -------------------------------------------
(1) (この時点では, 各レジスタには以下の値が入っているはず)
---------------------------------------- -}
// rbx: methodOop
// r13: sender sp
{- -------------------------------------------
(1) (ここからが生成するコードの始まり)
---------------------------------------- -}
address entry_point = __ pc();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
const Address size_of_parameters(rbx, methodOopDesc::
size_of_parameters_offset());
const Address invocation_counter(rbx, methodOopDesc::
invocation_counter_offset() +
InvocationCounter::counter_offset());
const Address access_flags (rbx, methodOopDesc::access_flags_offset());
{- -------------------------------------------
(1) コード生成:
「引数の数を取得する」
---------------------------------------- -}
// get parameter size (always needed)
__ load_unsigned_short(rcx, size_of_parameters);
{- -------------------------------------------
(1) (ネイティブメソッドの場合は, スタックを伸ばさないのでスタックオーバーフローのチェックは不要)
(See: InterpreterGenerator::generate_normal_entry())
---------------------------------------- -}
// native calls don't need the stack size check since they have no
// expression stack and the arguments are already on the stack and
// we only add a handful of words to the stack
{- -------------------------------------------
(1) (この時点では, レジスタには以下の値が入っているはず)
---------------------------------------- -}
// rbx: methodOop
// rcx: size of parameters
// r13: sender sp
{- -------------------------------------------
(1) コード生成:
「リターンアドレスを rax レジスタに移しておく.」
---------------------------------------- -}
__ pop(rax); // get return address
{- -------------------------------------------
(1) (ネイティブメソッドの場合は, 局所変数領域を呼び出し元のフレーム内に確保する必要はない)
(See: InterpreterGenerator::generate_normal_entry())
---------------------------------------- -}
// for natives the size of locals is zero
{- -------------------------------------------
(1) コード生成:
「引数の位置の先頭アドレスを計算し, r14 にセットしておく」
---------------------------------------- -}
// compute beginning of parameters (r14)
__ lea(r14, Address(rsp, rcx, Address::times_8, -wordSize));
{- -------------------------------------------
(1) コード生成:
「スタックフレーム上に空のスロットを 2つ作っておく
(result_handler slot と oop temp 用)」
---------------------------------------- -}
// add 2 zero-initialized slots for native calls
// initialize result_handler slot
__ push((int) NULL_WORD);
// slot for oop temp
// (static native method holder mirror/jni oop result)
__ push((int) NULL_WORD);
{- -------------------------------------------
(1) コード生成: (ただし, JIT コンパイルのためにメソッドの呼び出し回数を数える必要がある場合のみ生成する.
より具体的には, UseCompiler オプションまたは CountCompiledCalls オプションが指定されている場合のみ生成)
「_invocation_counter フィールドのアドレスを rcx レジスタに入れておく」
(これは, 後で呼び出す InterpreterGenerator::generate_counter_incr() の生成コードが
rcx に _invocation_counter フィールドのアドレスが入っていることを想定しているため.
(See: InterpreterGenerator::generate_counter_incr()))
---------------------------------------- -}
if (inc_counter) {
__ movl(rcx, invocation_counter); // (pre-)fetch invocation count
}
{- -------------------------------------------
(1) コード生成:
「TemplateInterpreterGenerator::generate_fixed_frame() が生成するコードで
スタック上に新しいフレームを確保する」
---------------------------------------- -}
// initialize fixed part of activation frame
generate_fixed_frame(true);
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理)
---------------------------------------- -}
// make sure method is native & not abstract
#ifdef ASSERT
__ movl(rax, access_flags);
{
Label L;
__ testl(rax, JVM_ACC_NATIVE);
__ jcc(Assembler::notZero, L);
__ stop("tried to execute non-native method as native");
__ bind(L);
}
{
Label L;
__ testl(rax, JVM_ACC_ABSTRACT);
__ jcc(Assembler::zero, L);
__ stop("tried to execute abstract method in interpreter");
__ bind(L);
}
#endif
{- -------------------------------------------
(1) コード生成:
「カレントスレッドに対応する JavaThread の
JavaThread::do_not_unlock_if_synchronized フィールドを, true にしておく」
(synchronized メソッドの場合でも, lock_method() を呼ぶのはこの後なので,
この時点ではまだ BasicObjectLock を確保していない.
が, stack banging によって StackOverflowError が出ると, 例外処理で stack の巻き戻しが起こる可能性があり,
何もしないと (synchronized メソッドなので) unlock_if_synchronized_method() で解放処理が行われてしまう.
そこで, thread の do_not_unlock_if_synchronized ビットを立てておく.
これを立てておくと unlock_if_synchronized_method() 時に, このメソッドは見逃されるようになる.
See: [here](no3059F5A.html) for details)
---------------------------------------- -}
// Since at this point in the method invocation the exception handler
// would try to exit the monitor of synchronized methods which hasn't
// been entered yet, we set the thread local variable
// _do_not_unlock_if_synchronized to true. The remove_activation will
// check this flag.
const Address do_not_unlock_if_synchronized(r15_thread,
in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
__ movbool(do_not_unlock_if_synchronized, true);
{- -------------------------------------------
(1) コード生成: (ただし, JIT コンパイルのためにメソッドの呼び出し回数を数える必要がある場合のみ生成する.
より具体的には, UseCompiler オプションまたは CountCompiledCalls オプションが指定されている場合のみ生成)
「InterpreterGenerator::generate_counter_incr() が生成するコードで,
invocation counter 値の増加処理と値のチェック処理を行う.
値に応じて, 以下の3通りに分岐.
* JIT コンパイル対象になる閾値を超えている場合:
invocation_counter_overflow ラベルまでジャンプ
* 監視対象になる閾値を超えているが, まだ mdp が確保されていない場合:
profile_method ラベルまでジャンプ
* それ以外の場合:
このまま以下の処理にフォールスルー」
(See: no3718SNC)
---------------------------------------- -}
// increment invocation count & check for overflow
Label invocation_counter_overflow;
if (inc_counter) {
generate_counter_incr(&invocation_counter_overflow, NULL, NULL);
}
{- -------------------------------------------
(1) コード生成:
「(ここが continue_after_compile ラベルの位置)」
---------------------------------------- -}
Label continue_after_compile;
__ bind(continue_after_compile);
{- -------------------------------------------
(1) コード生成:
「AbstractInterpreterGenerator::bang_stack_shadow_pages() が生成するコードで
stack overflow をチェックする. (stack banging コード)」
(See: [here](no3059d9W.html) for details)
---------------------------------------- -}
bang_stack_shadow_pages(true);
{- -------------------------------------------
(1) コード生成:
「stack banging が終わったので, do_not_unlock_if_synchronized ビットを元に戻す」
---------------------------------------- -}
// reset the _do_not_unlock_if_synchronized flag
__ movbool(do_not_unlock_if_synchronized, false);
{- -------------------------------------------
(1) コード生成: (引数で synchronized method だと指定されている場合にのみ生成)
「InterpreterGenerator::lock_method() が生成するコードで, ロックを取得する」
(See: [here](no9662EYy.html) for details)
---------------------------------------- -}
// check for synchronized methods
// Must happen AFTER invocation_counter check and stack overflow check,
// so method is not locked if overflows.
if (synchronized) {
lock_method();
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理) (引数で synchronized method だと指定されていない場合にのみ生成)
(本当に synchronized でないことを確認するコードを生成している)
---------------------------------------- -}
} else {
// no synchronization necessary
#ifdef ASSERT
{
Label L;
__ movl(rax, access_flags);
__ testl(rax, JVM_ACC_SYNCHRONIZED);
__ jcc(Assembler::zero, L);
__ stop("method needs synchronization");
__ bind(L);
}
#endif
}
{- -------------------------------------------
(1) (以降で, 実際にメソッド内の処理を開始)
---------------------------------------- -}
// start execution
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理)
---------------------------------------- -}
#ifdef ASSERT
{
Label L;
const Address monitor_block_top(rbp,
frame::interpreter_frame_monitor_block_top_offset * wordSize);
__ movptr(rax, monitor_block_top);
__ cmpptr(rax, rsp);
__ jcc(Assembler::equal, L);
__ stop("broken stack frame setup in interpreter");
__ bind(L);
}
#endif
{- -------------------------------------------
(1) コード生成: (JVMTI のフック点)
---------------------------------------- -}
// jvmti support
__ notify_method_entry();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
// work registers
const Register method = rbx;
const Register t = r11;
{- -------------------------------------------
(1) コード生成:
「引数の大きさを取得し, その分だけの領域をスタック上に確保しておく」
---------------------------------------- -}
// allocate space for parameters
__ get_method(method);
__ verify_oop(method);
__ load_unsigned_short(t,
Address(method,
methodOopDesc::size_of_parameters_offset()));
__ shll(t, Interpreter::logStackElementSize);
__ subptr(rsp, t);
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
__ andptr(rsp, -16); // must be 16 byte boundary (see amd64 ABI)
{- -------------------------------------------
(1) コード生成:
「呼び出し先の methodOop オブジェクト内から (signature_handler フィールドに格納されている) シグネチャハンドラを取得する.
もし signature_handler フィールドが空であれば, InterpreterRuntime::prepare_native_call() を呼び出して
methodOop オブジェクトにシグネチャハンドラを設定した後で, 改めて取得する.」
---------------------------------------- -}
// get signature handler
{
Label L;
__ movptr(t, Address(method, methodOopDesc::signature_handler_offset()));
__ testptr(t, t);
__ jcc(Assembler::notZero, L);
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::prepare_native_call),
method);
__ get_method(method);
__ movptr(t, Address(method, methodOopDesc::signature_handler_offset()));
__ bind(L);
}
{- -------------------------------------------
(1) コード生成:
「シグネチャハンドラを呼び出す.
(これによって, インタープリタのオペランドスタック内に格納されている引数を
ネイティブメソッドの ABI で規定されているレジスタやスタック上にコピーする)」
---------------------------------------- -}
// call signature handler
assert(InterpreterRuntime::SignatureHandlerGenerator::from() == r14,
"adjust this code");
assert(InterpreterRuntime::SignatureHandlerGenerator::to() == rsp,
"adjust this code");
assert(InterpreterRuntime::SignatureHandlerGenerator::temp() == rscratch1,
"adjust this code");
// The generated handlers do not touch RBX (the method oop).
// However, large signatures cannot be cached and are generated
// each time here. The slow-path generator can do a GC on return,
// so we must reload it after the call.
__ call(t);
{- -------------------------------------------
(1) コード生成:
「GC が発生したかもしれないので, rbx レジスタの値を再設定しておく」
---------------------------------------- -}
__ get_method(method); // slow path can do a GC, reload RBX
{- -------------------------------------------
(1) コード生成:
「リザルトハンドラーのアドレスをスタックフレーム上に待避しておく」
---------------------------------------- -}
// result handler is in rax
// set result handler
__ movptr(Address(rbp,
(frame::interpreter_frame_result_handler_offset) * wordSize),
rax);
{- -------------------------------------------
(1) コード生成:
「呼び出し先の methodOop 内を見て static メソッドかどうかを判定する.
static メソッドであれば, クラスオブジェクト(mirror object)を取得して Handle でくるみ, 引数は Handle にする.
(static メソッドでなければ何もしない)」
---------------------------------------- -}
// pass mirror handle if static call
{
Label L;
const int mirror_offset = klassOopDesc::klass_part_offset_in_bytes() +
Klass::java_mirror_offset_in_bytes();
__ movl(t, Address(method, methodOopDesc::access_flags_offset()));
__ testl(t, JVM_ACC_STATIC);
__ jcc(Assembler::zero, L);
// get mirror
__ movptr(t, Address(method, methodOopDesc::constants_offset()));
__ movptr(t, Address(t, constantPoolOopDesc::pool_holder_offset_in_bytes()));
__ movptr(t, Address(t, mirror_offset));
// copy mirror into activation frame
__ movptr(Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize),
t);
// pass handle to mirror
__ lea(c_rarg1,
Address(rbp, frame::interpreter_frame_oop_temp_offset * wordSize));
__ bind(L);
}
{- -------------------------------------------
(1) コード生成:
「呼び出し先の methodOop 内の native_function_offset から,
ネイティブメソッドのエントリポイントのアドレスを取得する.
もし native_function フィールドが空であれば, InterpreterRuntime::prepare_native_call() を呼び出して
methodOop オブジェクトにエントリポイントのアドレスを設定した後で, 改めて取得する.」
---------------------------------------- -}
// get native function entry point
{
Label L;
__ movptr(rax, Address(method, methodOopDesc::native_function_offset()));
ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry());
__ movptr(rscratch2, unsatisfied.addr());
__ cmpptr(rax, rscratch2);
__ jcc(Assembler::notEqual, L);
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::prepare_native_call),
method);
__ get_method(method);
__ verify_oop(method);
__ movptr(rax, Address(method, methodOopDesc::native_function_offset()));
__ bind(L);
}
{- -------------------------------------------
(1) コード生成:
「c_rarg0 に最後の引数 (JNIEnv*) を設定する.」
---------------------------------------- -}
// pass JNIEnv
__ lea(c_rarg0, Address(r15_thread, JavaThread::jni_environment_offset()));
{- -------------------------------------------
(1) コード生成:
「カレントスレッドの java frame anchor (Thread::_anchor フィールド) を設定する.」 #TODO
---------------------------------------- -}
// It is enough that the pc() points into the right code
// segment. It does not have to be the correct return pc.
__ set_last_Java_frame(rsp, rbp, (address) __ pc());
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理)
---------------------------------------- -}
// change thread state
#ifdef ASSERT
{
Label L;
__ movl(t, Address(r15_thread, JavaThread::thread_state_offset()));
__ cmpl(t, _thread_in_Java);
__ jcc(Assembler::equal, L);
__ stop("Wrong thread state in native stub");
__ bind(L);
}
#endif
{- -------------------------------------------
(1) コード生成:
「カレントスレッドの thread state (JavaThread::_thread_state フィールド) を,
_thread_in_native に設定する.」
---------------------------------------- -}
// Change state to native
__ movl(Address(r15_thread, JavaThread::thread_state_offset()),
_thread_in_native);
{- -------------------------------------------
(1) コード生成:
「ネイティブメソッドのエントリポイントを呼び出す.」
---------------------------------------- -}
// Call the native method.
__ call(rax);
{- -------------------------------------------
(1) (以下, ネイティブメソッドからリターンしてきた後の後始末処理.
返値は rax か xmm0 に入っている.)
---------------------------------------- -}
// result potentially in rax or xmm0
{- -------------------------------------------
(1)
---------------------------------------- -}
// Depending on runtime options, either restore the MXCSR
// register after returning from the JNI Call or verify that
// it wasn't changed during -Xcheck:jni.
if (RestoreMXCSROnJNICalls) {
__ ldmxcsr(ExternalAddress(StubRoutines::x86::mxcsr_std()));
}
else if (CheckJNICalls) {
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::verify_mxcsr_entry())));
}
{- -------------------------------------------
(1)
---------------------------------------- -}
// NOTE: The order of these pushes is known to frame::interpreter_frame_result
// in order to extract the result of a method call. If the order of these
// pushes change or anything else is added to the stack then the code in
// interpreter_frame_result must also change.
__ push(dtos);
__ push(ltos);
{- -------------------------------------------
(1) コード生成:
「カレントスレッドの thread state (JavaThread::_thread_state フィールド) を,
_thread_in_native_trans に設定する.」
なお, MP 環境の場合には
VM Thread 側への visibility (sequential consistency) を保証する必要があるので,
変更後に以下のどちらかを行っている (See: SafepointSynchronize::begin()).
* UseMembar オプションが指定されている場合:
「OrderAccess::fence() が生成するコードでメモリバリアを張る.」
* そうではない場合:
「InterfaceSupport::serialize_memory() が生成するコードで serialize page への書き込みを行う.」
こうしないと, 以降の safepoint チェックが
VM Thread による _state の変更と race した場合に, すり抜けてしまう恐れがある.
逆にこの順序で行うことで, race した場合でも VM Thread が見つけて
この Thread の suspend_flags を変更してくれるようにできている.
このため, SafepointSynchronize::_state と Thread::_suspend_flags の両方をチェックすれば,
すり抜けること無くチェックが行える.
---------------------------------------- -}
// change thread state
__ movl(Address(r15_thread, JavaThread::thread_state_offset()),
_thread_in_native_trans);
if (os::is_MP()) {
if (UseMembar) {
// Force this write out before the read below
__ membar(Assembler::Membar_mask_bits(
Assembler::LoadLoad | Assembler::LoadStore |
Assembler::StoreLoad | Assembler::StoreStore));
} else {
// Write serialization page so VM thread can do a pseudo remote membar.
// We use the current thread pointer to calculate a thread specific
// offset to write to within the page. This minimizes bus traffic
// due to cache line collision.
__ serialize_memory(r15_thread, rscratch2);
}
}
{- -------------------------------------------
(1) コード生成:
「もし Safepoint 停止が開始されていた場合,
JavaThread::check_special_condition_for_native_trans() を呼び出してここで block する. 」
(確認は SafepointSynchronize::_state をチェックすることで行っている.)
---------------------------------------- -}
// check for safepoint operation in progress and/or pending suspend requests
{
Label Continue;
__ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()),
SafepointSynchronize::_not_synchronized);
Label L;
__ jcc(Assembler::notEqual, L);
__ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0);
__ jcc(Assembler::equal, Continue);
__ bind(L);
// Don't use call_VM as it will see a possible pending exception
// and forward it and never return here preventing us from
// clearing _last_native_pc down below. Also can't use
// call_VM_leaf either as it will check to see if r13 & r14 are
// preserved and correspond to the bcp/locals pointers. So we do a
// runtime call by hand.
//
__ mov(c_rarg0, r15_thread);
__ mov(r12, rsp); // remember sp (can only use r12 if not using call_VM)
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
__ andptr(rsp, -16); // align stack as required by ABI
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)));
__ mov(rsp, r12); // restore sp
__ reinit_heapbase();
__ bind(Continue);
}
{- -------------------------------------------
(1) コード生成:
「(この時点では thread_in_native_trans になっているはずなので)
カレントスレッドの thread state (JavaThread::_thread_state フィールド) を _thread_in_Java に戻しておく.」
---------------------------------------- -}
// change thread state
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java);
{- -------------------------------------------
(1) コード生成:
「カレントスレッドの java frame anchor (Thread::_anchor フィールド) をクリアする」
---------------------------------------- -}
// reset_last_Java_frame
__ reset_last_Java_frame(true, true);
{- -------------------------------------------
(1) コード生成:
「JNI local handles を解放する.
(もっと具体的に言うと, JNIHandleBlock::_top の値を 0 に変更する.
これにより JNIHandleBlock からの参照が無くなるため, GC で回収されるようになる.)」
---------------------------------------- -}
// reset handle block
__ movptr(t, Address(r15_thread, JavaThread::active_handles_offset()));
__ movptr(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD);
{- -------------------------------------------
(1) コード生成:
「もし返り値が oop 型で, かつ値も null でなければ,
(GC の調査対象に入れておかないと勝手に dangling pointer にされてしまうかもしれないので)
スタックフレーム中の frame::interpreter_frame_oop_temp_offset の位置にコピーしておく.」
---------------------------------------- -}
// If result is an oop unbox and store it in frame where gc will see it
// and result handler will pick it up
{
Label no_oop, store_result;
__ lea(t, ExternalAddress(AbstractInterpreter::result_handler(T_OBJECT)));
__ cmpptr(t, Address(rbp, frame::interpreter_frame_result_handler_offset*wordSize));
__ jcc(Assembler::notEqual, no_oop);
// retrieve result
__ pop(ltos);
__ testptr(rax, rax);
__ jcc(Assembler::zero, store_result);
__ movptr(rax, Address(rax, 0));
__ bind(store_result);
__ movptr(Address(rbp, frame::interpreter_frame_oop_temp_offset*wordSize), rax);
// keep stack depth as expected by pushing oop which will eventually be discarde
__ push(ltos);
__ bind(no_oop);
}
{- -------------------------------------------
(1) コード生成:
「スタック上の yellow page が disabled になっていたら
SharedRuntime::reguard_yellow_pages() を呼んで元に戻す」
---------------------------------------- -}
{
Label no_reguard;
__ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()),
JavaThread::stack_guard_yellow_disabled);
__ jcc(Assembler::notEqual, no_reguard);
__ pusha(); // XXX only save smashed registers
__ mov(r12, rsp); // remember sp (can only use r12 if not using call_VM)
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
__ andptr(rsp, -16); // align stack as required by ABI
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)));
__ mov(rsp, r12); // restore sp
__ popa(); // XXX only restore smashed registers
__ reinit_heapbase();
__ bind(no_reguard);
}
{- -------------------------------------------
(1) コード生成:
「method レジスタの値をセットし直す」
---------------------------------------- -}
// The method register is junk from after the thread_in_native transition
// until here. Also can't call_VM until the bcp has been
// restored. Need bcp for throwing exception below so get it now.
__ get_method(method);
__ verify_oop(method);
{- -------------------------------------------
(1) コード生成:
「r13(bcp) の値をセットし直す」
---------------------------------------- -}
// restore r13 to have legal interpreter frame, i.e., bci == 0 <=>
// r13 == code_base()
__ movptr(r13, Address(method, methodOopDesc::const_offset())); // get constMethodOop
__ lea(r13, Address(r13, constMethodOopDesc::codes_offset())); // get codebase
{- -------------------------------------------
(1) コード生成:
「カレントスレッドの pending_exception フィールドを確認し,
ネイティブメソッド呼び出し中に例外が発生したかどうかをチェックする.
もし 0 でなければ, 例外が発生したということなので
MacroAssembler::call_VM() が生成するコードでランタイムを呼び出して伝搬させる.
(呼び出し先は InterpreterRuntime::throw_pending_exception() だが,
MacroAssembler::call_VM() のチェックコードで引っかけるのが目的.
InterpreterRuntime::throw_pending_exception() 自体は何もしない.)」
(See: [here](no3059d4M.html) for details)
---------------------------------------- -}
// handle exceptions (exception handling will handle unlocking!)
{
Label L;
__ cmpptr(Address(r15_thread, Thread::pending_exception_offset()), (int32_t) NULL_WORD);
__ jcc(Assembler::zero, L);
// Note: At some point we may want to unify this with the code
// used in call_VM_base(); i.e., we should use the
// StubRoutines::forward_exception code. For now this doesn't work
// here because the rsp is not correctly set at this point.
__ MacroAssembler::call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::throw_pending_exception));
__ should_not_reach_here();
__ bind(L);
}
{- -------------------------------------------
(1) コード生成: (引数で synchronized method だと指定されている場合にのみ生成)
「InterpreterGenerator::unlock_method() が生成するコードで, ロックを開放する」
(See: [here](no3059F5A.html) for details)
---------------------------------------- -}
// do unlocking if necessary
{
Label L;
__ movl(t, Address(method, methodOopDesc::access_flags_offset()));
__ testl(t, JVM_ACC_SYNCHRONIZED);
__ jcc(Assembler::zero, L);
// the code below should be shared with interpreter macro
// assembler implementation
{
Label unlock;
// 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.
const Address monitor(rbp,
(intptr_t)(frame::interpreter_frame_initial_sp_offset *
wordSize - sizeof(BasicObjectLock)));
// monitor expect in c_rarg1 for slow unlock path
__ lea(c_rarg1, monitor); // address of first monitor
__ movptr(t, Address(c_rarg1, BasicObjectLock::obj_offset_in_bytes()));
__ testptr(t, t);
__ jcc(Assembler::notZero, unlock);
// Entry already unlocked, need to throw exception
__ MacroAssembler::call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::throw_illegal_monitor_state_exception));
__ should_not_reach_here();
__ bind(unlock);
__ unlock_object(c_rarg1);
}
__ bind(L);
}
{- -------------------------------------------
(1) コード生成: (JVMTI のフック点)
---------------------------------------- -}
// jvmti support
// Note: This must happen _after_ handling/throwing any exceptions since
// the exception handler code notifies the runtime of method exits
// too. If this happens before, method entry/exit notifications are
// not properly paired (was bug - gri 11/22/99).
__ notify_method_exit(vtos, InterpreterMacroAssembler::NotifyJVMTI);
{- -------------------------------------------
(1)
---------------------------------------- -}
// restore potential result in edx:eax, call result handler to
// restore potential result in ST0 & handle result
__ pop(ltos);
__ pop(dtos);
{- -------------------------------------------
(1) コード生成:
「スタックフレーム上に待避していた result handler を呼び出す」
---------------------------------------- -}
__ movptr(t, Address(rbp,
(frame::interpreter_frame_result_handler_offset) * wordSize));
__ call(t);
{- -------------------------------------------
(1) コード生成:
「SP の値をスタックフレーム上に待避しておいた値(= 呼び出し元の SP)に復帰させ,
スタックフレームを破棄して, リターン」
---------------------------------------- -}
// remove activation
__ movptr(t, Address(rbp,
frame::interpreter_frame_sender_sp_offset *
wordSize)); // get sender sp
__ leave(); // remove frame anchor
__ pop(rdi); // get return address
__ mov(rsp, t); // set sp to sender sp
__ jmp(rdi);
{- -------------------------------------------
(1) (以下は, invocation counter の値が閾値を超えていた際の処理パス)
---------------------------------------- -}
{- -------------------------------------------
(1) コード生成:
「InterpreterGenerator::generate_counter_overflow() が生成するコードにより,
JIT コンパイラを呼び出す (See: ...#TODO)
その後, continue_after_compile ラベルに戻って実行を再開する.」
---------------------------------------- -}
if (inc_counter) {
// Handle overflow of counter and compile method
__ bind(invocation_counter_overflow);
generate_counter_overflow(&continue_after_compile);
}
{- -------------------------------------------
(1) 以上で生成したコードの先頭アドレスをリターン.
---------------------------------------- -}
return entry_point;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.