関数フレームの先頭にはスレッドの先頭アドレス(テンプレートトップ)が 入り, それ以外の場所は主に引数の受け渡しとして用いられたり, ローカル (auto)変数領域等として用いられる.
また, フリーリスト中の関数フレームの先頭には次の(フリーな)関数フレー ムのポインタが入っている.
この 9 bit は関数フレームの大きさ(128 word)で, オフセットが 9 bit 固定 であるため、一つの関数フレームが制御できるスレッドのエントリアドレスは スレッドの先頭から 128 word までとなる. ここで, 関数フレームの先頭はパ ケットアドレス部(fp)の下位 9 bit を 0 としたアドレスである.
deq r1, r2, r3 r3 <- MEM(r1) MEM(r1) <- r2r1 が関数フレームの先頭を指していない場合は, r1 の下 位 9 bitを 0 とする特殊な deqr 命令を用いる.
deqr r1, r2, r3 e <- (r1 & 0xffffe00) r3 <- MEM(e) MEM(e) <- r2関数フレームは 128 word 固定なので r1 & 0xffffe00 で関数 フレームの先頭を得ることができる.
関数呼び出しを行う場合は deq (deqr)命令により関数フ レームを一つ allocate して, その関数フレームに必要な情報をいれ, そのフ レームへパケットを投げることにより関数呼び出しを行う.
その逆の操作が enq であり, enq ではもう使わない関数 フレームをフリーリストに戻す. この時, 関数フレームの先頭に ftop のアドレスを書き込むとともに, ftop に fp の値(ただし下位 9 bitは 0 )を代入する. この操作により, ftop の指しているメモリにはその次のフリーリストのアドレスが入 り, ftop は次のフリーリストのアドレスを指している.
enq r1, r2, r3 MEM(r1) <- r2 r3 <- r1deqr 命令と同様に r1 が関数フレームの先頭を指してい ない場合は enqr 命令を使う.
enqr r1, r2, r3 e <- (r1 & 0xffffe00) MEM(e) <- r2 r3 <- e
例えば _sinval を新たな関数フレームの先頭アドレスに入れるため に deqr 命令を用い,
deqr ftop, _sinval, ftopとすれば, 次の関数フレームの先頭 (ftop & 0xffffe00) に, _sinvalが入り, ftop には次の関数フレームの先頭アドレ スの内容(その次のフリーな関数フレームのアドレスが入っている)が入る. 関 数フレームをフリーリストに戻すためには, enqr 命令を用い,
enqr fp, ftop, ftopとする. この時, 関数フレームの先頭 (fp & 0xffffe00) に ftop が入り, ftop には関数フレームの先頭のアドレスが 入る.
(*1) 初期状態では 0 であるが, page fault 起こ るたびに新たに 16 エントリが作られる.
excc では関数呼び出しをするとき, 引数は(呼び出し先の)関数 フレームのオフセット 4 byte から順番に入れ, また戻りアドレス(スレッド 再開アドレス, continuation)はパケットデータ部分として与える.
sr ftop, 0x4, r0 senda0 ftop, fp, L1, 0 deqr ftop, __function_you_want_to_call, ftop L1:この例では, r0 を第一引数として与え, L1 のアドレスを continuation (パケットデータ部)として ftop (パケットアドレス 部)にむかってパケットを投げている. senda0 を用い, continuation を作っていることに注意. continuation の生成には他に lpa 命令も用いられる(*2). 次の deqr 命令で ftop の内容に __function_you_want_to_call のアドレスを書き込み, ftop を更新している. deqr 命令の方が必ず senda0 によるスレッド起動より早くに実行されることに注意.
callee では pr0 に戻りアドレスが入っていることになり, 引数 は fp + 4, fp + 8, ... に入っていることになる(*3).
lr fp, 4, r0 lr fp, 8, r1この例では, 第一引数を r0 に, 第二引数を r1 に入れて いる. callee はまず戻りアドレスを自分の関数フレームのオフセット 0x1fc に待避する(が, これは必須ではない).
sr fp, 0x1fc, pr0return するときは, 戻り値の(はじめの) 4 byte はパケットデータ部分に入 れる. r0 に戻り値がはいっている場合, 以下のようになる.
lr fp, 0x1fc, pr0 send2 r0, pr0, 0, 0 enqr fp, ftop, ftoppr0 に既に continuation が入っている場合は始めの lr 命令は必要ない. continuation (パケットアドレス部)が既に得られているの で, send2 を用いる. また, この関数フレームはもう必要ないため, enqr 命令によりフリーリストに戻している. double の場 合は, 戻り先の関数フレームのオフセット 0x1f4 byte に書いて戻 す.
(*2) send[0-3](.pt), senda[0-3](.pt), lpa[0-3](.pt) 命令等については細粒度パケッ ト通信のページを参照.
(*3) 細かいことを言えば, fp は関数 フレームの先頭を指しているとは限らないので, (fp & 0xffffe00) である. この様なアドレッシングを用いるために, sr 命令, lr 命令などが用意されている.
send1.pt r0, r1, 0, 0, 0x23 ; H_GETSEGここで, r0 は continuatuion, r1 は実行したい(テンプ レートトップに書き込みたい)スレッドの先頭アドレスである. 確保した関数 フレームのグローバルアドレスは pr0 に入っている.
オペランドセグメントを確保した後, リモートメモリ書き込みのスペシャ ルパケット SYSWR を用い引数等を書き,
send0.pt r0, pr0, 0x4, 0, 0x24 ; SYSWRローカルの関数呼び出し同様パケットを送出し呼び出す. ここで, r0 は書き込みたい引数, pr0 は確保した関数フレームの 先頭である. この時パケットデータ部として戻りアドレス(continuation)を送 出する.
callee はローカル関数呼び出しの時と同じである.
senda1.pt pr0, zr, 0, 0, 0x22 ; H_NULLここで, pr0 は fork する関数の関数フレームの先頭であり, zr は無視されるためなんでも良い.
この時 callee では return の時にスペシャルパケット H_NULL を送出することになる. H_NULL は何もしない特殊パケットであり, これにより fork を行なった caller が終了してオペランドセグメントが回収 されたあとに callee からパケットが届いても正しい動作が保証できる. 一方, callee の立場としては, 自分が remote call として呼ばれたのか fork で呼 ばれたのかを区別しなくてすむため処理が簡素化され, オーバヘッドが少なく てすむ.
Osamu Tatebe <tatebe@etl.go.jp> Last modified: Thu Dec 9 14:12:58 1999