hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp
//----------------------------------------------------------------------------------------------------
// Call stubs are used to call Java from C
address generate_call_stub(address& return_pc) {
{- -------------------------------------------
(1) (以下, StubCodeMark によるスタブ生成を行う) (See: StubCodeMark)
---------------------------------------- -}
StubCodeMark mark(this, "StubRoutines", "call_stub");
{- -------------------------------------------
(1) (ここからが生成するコードの始まり)
---------------------------------------- -}
address start = __ pc();
{- -------------------------------------------
(1) (このコードが実行される時点で, レジスタには以下のような値が入っている.
またスタックフレームの状態も以下の通り.
なお, 第一引数(call wrapper address, link argument)の位置を変える場合は
frame::entry_frame_call_wrapper() の中身も変えるように, とのこと.)
---------------------------------------- -}
// Incoming arguments:
//
// o0 : call wrapper address
// o1 : result (address)
// o2 : result type
// o3 : method
// o4 : (interpreter) entry point
// o5 : parameters (address)
// [sp + 0x5c]: parameter size (in words)
// [sp + 0x60]: thread
//
// +---------------+ <--- sp + 0
// | |
// . reg save area .
// | |
// +---------------+ <--- sp + 0x40
// | |
// . extra 7 slots .
// | |
// +---------------+ <--- sp + 0x5c
// | param. size |
// +---------------+ <--- sp + 0x60
// | thread |
// +---------------+
// | |
// note: if the link argument position changes, adjust
// the code in frame::entry_frame_call_wrapper()
const Argument link = Argument(0, false); // used only for GC
const Argument result = Argument(1, false);
const Argument result_type = Argument(2, false);
const Argument method = Argument(3, false);
const Argument entry_point = Argument(4, false);
const Argument parameters = Argument(5, false);
const Argument parameter_size = Argument(6, false);
const Argument thread = Argument(7, false);
{- -------------------------------------------
(1) コード生成:
「これから Java コードの実行が始まるので, G2_thread レジスタの値を適切に設定しておく.
(カレントスレッドに対応する JavaThread のアドレスが
第8引数として渡されているはずなので, それをロード)」
---------------------------------------- -}
// setup thread register
__ ld_ptr(thread.as_address(), G2_thread);
{- -------------------------------------------
(1) コード生成:
「G6_heapbase レジスタに正しい値をセットしておく」
(See: [here](no289165bb.html) for details)
---------------------------------------- -}
__ reinit_heapbase();
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理)
---------------------------------------- -}
#ifdef ASSERT
// make sure we have no pending exceptions
{ const Register t = G3_scratch;
Label L;
__ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), t);
__ br_null(t, false, Assembler::pt, L);
__ delayed()->nop();
__ stop("StubRoutines::call_stub: entered with pending exception");
__ bind(L);
}
#endif
{- -------------------------------------------
(1) コード生成:
「確保するフレームサイズを計算し, save 命令で SP レジスタを下げてフレーム確保を行う.
(save 命令を使っているので, レジスタの待避も同時に行われる)」
(なお, フレームサイズは以下のように計算している.
フレームサイズ = round_to(「実際に呼び出す Java メソッドの引数のサイズ」 + 「その他のフレーム内に必要なスペース」)
式中の値は, それぞれ以下のようにして取得する.
* 「実際に呼び出す Java メソッドの引数のサイズ」
このスタブコードに第7引数として渡されてくる.
* 「その他のフレーム内に必要なスペース」
frame::memory_parameter_word_sp_offset の値を使用.
(なお, この値は基本的には Sparc の calling convention 通りになっており,
window save area と第6引数までを格納できるスペース量に等しい. = 22ワード分))
(このコードが実行された後のスタックの様子は, 以下のコメントに書かれている図を参照)
---------------------------------------- -}
// create activation frame & allocate space for parameters
{ const Register t = G3_scratch;
__ ld_ptr(parameter_size.as_address(), t); // get parameter size (in words)
__ add(t, frame::memory_parameter_word_sp_offset, t); // add space for save area (in words)
__ round_to(t, WordsPerLong); // make sure it is multiple of 2 (in words)
__ sll(t, Interpreter::logStackElementSize, t); // compute number of bytes
__ neg(t); // negate so it can be used with save
__ save(SP, t, SP); // setup new frame
}
// +---------------+ <--- sp + 0
// | |
// . reg save area .
// | |
// +---------------+ <--- sp + 0x40
// | |
// . extra 7 slots .
// | |
// +---------------+ <--- sp + 0x5c
// | empty slot | (only if parameter size is even)
// +---------------+
// | |
// . parameters .
// | |
// +---------------+ <--- fp + 0
// | |
// . reg save area .
// | |
// +---------------+ <--- fp + 0x40
// | |
// . extra 7 slots .
// | |
// +---------------+ <--- fp + 0x5c
// | param. size |
// +---------------+ <--- fp + 0x60
// | thread |
// +---------------+
// | |
{- -------------------------------------------
(1) コード生成:
「呼び出す Java メソッドに引数がある場合は, スタックの局所変数領域上にコピーしておく.」
(スタブコードが呼ばれた時点では, Java メソッド用の引数は JavaCallArguments オブジェクト内に格納されている.
そして, スタブコードの第6引数がその JavaCallArguments オブジェクトを指している.
局所変数領域は FP の直下になるので, もし引数がある場合にはその位置にコピーする.)
(なお引数の並び方は, FP の直下が第一引数, その下が第二引数, ..., という順で並ぶ.
詰める作業は第一引数から開始し, 順に下のアドレスへと詰めている.)
---------------------------------------- -}
// pass parameters if any
BLOCK_COMMENT("pass parameters if any");
{ const Register src = parameters.as_in().as_register();
const Register dst = Lentry_args;
const Register tmp = G3_scratch;
const Register cnt = G4_scratch;
// test if any parameters & setup of Lentry_args
Label exit;
{- -------------------------------------------
(1.1) (呼び出す Java メソッドの引数の個数が 0 ならば何もしない (exit ラベルにジャンプ).
そうでなければループに突入.)
(なお, どちらの場合もこの時点で, Lentry_args (= Lesp) に
FP 直下のアドレス(= FP レジスタの値 + STACK BIAS 値)を設定している.
ループを回す場合はループ中で Lentry_args の値を下げていくため,
引数の個数に関わらず, この処理が終わった時点で
Lentry_args (= Lesp) はオペランドスタック領域の先頭アドレスにセットされることになる)
---------------------------------------- -}
__ ld_ptr(parameter_size.as_in().as_address(), cnt); // parameter counter
__ add( FP, STACK_BIAS, dst );
__ tst(cnt);
__ br(Assembler::zero, false, Assembler::pn, exit);
__ delayed()->sub(dst, BytesPerWord, dst); // setup Lentry_args
// copy parameters if any
Label loop;
{- -------------------------------------------
(1.1) (ここからがループの始まり. 引数を Lentry_args が指す局所変数領域へとコピーしていく)
---------------------------------------- -}
__ BIND(loop);
// Store parameter value
__ ld_ptr(src, 0, tmp);
__ add(src, BytesPerWord, src);
__ st_ptr(tmp, dst, 0);
__ deccc(cnt);
__ br(Assembler::greater, false, Assembler::pt, loop);
__ delayed()->sub(dst, Interpreter::stackElementSize, dst);
{- -------------------------------------------
(1.1) (ここまでがループ)
---------------------------------------- -}
// done
__ BIND(exit);
}
{- -------------------------------------------
(1) (以下で, Gargs 等のセットアップと実際の呼び出し処理を行う)
---------------------------------------- -}
// setup parameters, method & call Java function
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
コード生成: (デバッグ用の処理)
---------------------------------------- -}
#ifdef ASSERT
// layout_activation_impl checks it's notion of saved SP against
// this register, so if this changes update it as well.
const Register saved_SP = Lscratch;
__ mov(SP, saved_SP); // keep track of SP before call
#endif
{- -------------------------------------------
(1) コード生成:
「Gargs レジスタに最終引数のアドレスを設定」
(呼び出される側がこの値が入っていることを想定しているため)
---------------------------------------- -}
// setup parameters
const Register t = G3_scratch;
__ ld_ptr(parameter_size.as_in().as_address(), t); // get parameter size (in words)
__ sll(t, Interpreter::logStackElementSize, t); // compute number of bytes
__ sub(FP, t, Gargs); // setup parameter pointer
#ifdef _LP64
__ add( Gargs, STACK_BIAS, Gargs ); // Account for LP64 stack bias
#endif
{- -------------------------------------------
(1) コード生成:
「この時点での SP の値を O5_savedSP レジスタに退避しておく」
---------------------------------------- -}
__ mov(SP, O5_savedSP);
{- -------------------------------------------
(1) コード生成:
「jmpl 命令により, 第5引数で指定されたエントリポイントを呼び出す.
(なお, リターンアドレスは O7 に記録しているので, call 命令とほぼ同じ)
(また, 遅延スロットを使うことで, 第4引数で渡された methodOop のアドレスを
G5_method レジスタにコピーする処理も行っている)」
---------------------------------------- -}
// do the call
//
// the following register must be setup:
//
// G2_thread
// G5_method
// Gargs
BLOCK_COMMENT("call Java function");
__ jmpl(entry_point.as_in().as_register(), G0, O7);
__ delayed()->mov(method.as_in().as_register(), G5_method); // setup method
{- -------------------------------------------
(1) (デバッグ用の処理) (See: BLOCK_COMMENT)
---------------------------------------- -}
BLOCK_COMMENT("call_stub_return_address:");
{- -------------------------------------------
(1) この時点でのコードのアドレスを取得し, 引数で渡された return_pc に記録しておく.
(これは, 上の jmpl による呼び出しのリターンアドレスに相当.
return_pc は参照渡しであり, この値は呼び出し元から使用される)
---------------------------------------- -}
return_pc = __ pc();
{- -------------------------------------------
(1) (なお, 呼び出し先がインタープリタ実行ではない場合 (= i2c スタブなどが挟まった場合),
リターンしてきた時点で SP がずれているかもしれないので注意)
---------------------------------------- -}
// The callee, if it wasn't interpreted, can return with SP changed so
// we can no longer assert of change of SP.
{- -------------------------------------------
(1) コード生成:
「呼び出した Java メソッドの返値を, 第2引数で指定されたアドレスに書き込む.
その後, restore 命令でフレームの破棄とレジスタの復帰を行いつつ, 呼び出し元にリターン.
(なお, 返値の型に応じて適切なストア命令を実行する.
返値の型に関する情報は第3引数で渡されてくるので, その情報を動的にチェックして適切なストア命令を選ぶ)」
---------------------------------------- -}
// store result depending on type
// (everything that is not T_OBJECT, T_LONG, T_FLOAT, or T_DOUBLE
// is treated as T_INT)
{ const Register addr = result .as_in().as_register();
const Register type = result_type.as_in().as_register();
Label is_long, is_float, is_double, is_object, exit;
__ cmp(type, T_OBJECT); __ br(Assembler::equal, false, Assembler::pn, is_object);
__ delayed()->cmp(type, T_FLOAT); __ br(Assembler::equal, false, Assembler::pn, is_float);
__ delayed()->cmp(type, T_DOUBLE); __ br(Assembler::equal, false, Assembler::pn, is_double);
__ delayed()->cmp(type, T_LONG); __ br(Assembler::equal, false, Assembler::pn, is_long);
__ delayed()->nop();
// store int result
__ st(O0, addr, G0);
__ BIND(exit);
__ ret();
__ delayed()->restore();
__ BIND(is_object);
__ ba(false, exit);
__ delayed()->st_ptr(O0, addr, G0);
__ BIND(is_float);
__ ba(false, exit);
__ delayed()->stf(FloatRegisterImpl::S, F0, addr, G0);
__ BIND(is_double);
__ ba(false, exit);
__ delayed()->stf(FloatRegisterImpl::D, F0, addr, G0);
__ BIND(is_long);
#ifdef _LP64
__ ba(false, exit);
__ delayed()->st_long(O0, addr, G0); // store entire long
#else
#if defined(COMPILER2)
// All return values are where we want them, except for Longs. C2 returns
// longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1.
// Since the interpreter will return longs in G1 and O0/O1 in the 32bit
// build we simply always use G1.
// Note: I tried to make c2 return longs in O0/O1 and G1 so we wouldn't have to
// do this here. Unfortunately if we did a rethrow we'd see an machepilog node
// first which would move g1 -> O0/O1 and destroy the exception we were throwing.
__ ba(false, exit);
__ delayed()->stx(G1, addr, G0); // store entire long
#else
__ st(O1, addr, BytesPerInt);
__ ba(false, exit);
__ delayed()->st(O0, addr, G0);
#endif /* COMPILER2 */
#endif /* _LP64 */
}
{- -------------------------------------------
(1) 以上で生成したコードの先頭アドレスをリターン.
---------------------------------------- -}
return start;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.