Calling Convention

このページでは excc の(リモート)関数呼び出しのコンベンション および関数呼び出し(スレッド生成)時に用いられる関数フレームの管理につい て述べる.

関連するレジスタ

スレッド起動, 関数フレームの管理に関連するレジスタは以下の通りである.
ftop
次のフリーな関数フレームの先頭を指すために使われる.
fp
パケットのアドレス部分が入る. (現在の関数フレーム(の先頭を) 指すために使われる.)
pr0
パケットのデータ部分が入る. (continuation, return value と して使われる.)

関数フレーム

スレッドごとに 128 word (512 byte)の関数フレームが割り当てられ, オペラ ンドセグメントと呼ばれる. 上の ftop, fp レジスタおよ びこれから説明する enq, deq (enqr, deqr)命令により(フリー)リストとして管理されている.

関数フレームの先頭にはスレッドの先頭アドレス(テンプレートトップ)が 入り, それ以外の場所は主に引数の受け渡しとして用いられたり, ローカル (auto)変数領域等として用いられる.

また, フリーリスト中の関数フレームの先頭には次の(フリーな)関数フレー ムのポインタが入っている.

(リモート)スレッド起動

パケットがプロセッサに到着すると, 入力バッファに格納され, 到着した順番 に処理される. (ただしリモートメモリ書き込み, リモートメモリ参照パケッ トを除く.) パケットの処理が開始されると, fp レジスタにパケッ トアドレス部が, pr0 レジスタにパケットデータ部が格納され る. パケットの処理は, 関数フレームの先頭に書かれているアドレス(テン プレートトップ)にアドレス部の下位 9 bit (ただし下位 2 bit は 0 )を加え たアドレスの命令から開始される.

この 9 bit は関数フレームの大きさ(128 word)で, オフセットが 9 bit 固定 であるため、一つの関数フレームが制御できるスレッドのエントリアドレスは スレッドの先頭から 128 word までとなる. ここで, 関数フレームの先頭はパ ケットアドレス部(fp)の下位 9 bit を 0 としたアドレスである.

enq, deq (enqr, deqr)命令

関数フレームは ftop, fp を用いフリーリストとして管理 されている. ftop の指しているメモリには次のフリーな関数フレー ムのアドレスが入っている(*1). 関数フレームをフ リーリストから取り出す場合は, ftop のアドレス先にレジスタをコ ピーするとともに, ftop をそのアドレス先の内容で置き換える.こ の操作を行うのが, deq 命令である.
    deq  r1, r2, r3

    r3      <- MEM(r1)
    MEM(r1) <- r2
r1 が関数フレームの先頭を指していない場合は, 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 のアドレスを書き込むとともに, ftopfp の値(ただし下位 9 bitは 0 )を代入する. この操作により, ftop の指しているメモリにはその次のフリーリストのアドレスが入 り, ftop は次のフリーリストのアドレスを指している.

    enq  r1, r2, r3

    MEM(r1) <- r2
    r3      <- r1
deqr 命令と同様に 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, pr0
return するときは, 戻り値の(はじめの) 4 byte はパケットデータ部分に入 れる. r0 に戻り値がはいっている場合, 以下のようになる.
    lr		fp, 0x1fc, pr0
    send2	r0, pr0, 0, 0
    enqr	fp, ftop, ftop
pr0 に既に 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 命令などが用意されている.

リモート関数呼び出し

リモートの関数を呼び出すためには, まずリモートのオペランドセグメントを 確保する必要がある. この操作のために getseg と呼ばれるスペシャ ルパケットが用意されている. スペシャルパケットを投げるためには, 主に send0.pt あるいは send1.pt が用いられる.
    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 はローカル関数呼び出しの時と同じである.

fork

(remote) fork は関数呼び出しと同様にオペランドセグメントを確保し, 引数 を SYSWR した後パケットを投げるが, continuation にはスペシャ ルパケット H_NULL を入れる. このパケット送出のためには senda1.pt が便利である.
    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 で呼 ばれたのかを区別しなくてすむため処理が簡素化され, オーバヘッドが少なく てすむ.


方式 (Japanese | English ) [ EMX ( English | Japanese ) ]

Osamu Tatebe <tatebe@etl.go.jp>
Last modified: Thu Dec 9 14:12:58 1999