Mosh のスタックフレーム構造 - Mosh 開発
スタックフレームの構造を変更したのでメモ。
書いておかないと絶対忘れる自信がある。
スタックフレームは大きくわけて2種類。
- 手続き呼び出しフレーム
- let フレーム
let フレームは手続き呼び出しフレームよりも積むものが少ないので多少軽い。
手続き呼び出しフレーム
関連する命令は、FRAME, CALL, RETURN。
FRAME は以下の順で以下のものを push する。
- CALL 後の行き先(Continuation)
- current closure ポインタ(現在呼び出されているクロージャのポインタ)
- frame ポインタ
このとき frame ポインタはまだ更新されていない。
FRAME 後は引数が積まれる、(proc arg0 arg1) なら arg0, arg1 の順に積まれる。
CALL ではオプショナル引数の調整などを行いつつ frame ポインタを引数を積む直前に設定する。
つまりこの時点でスタックは
======================================== continuation ======================================== curren closure ======================================== prev frame pointer ======================================== <=== frame pointer arg0 ======================================== arg1 ======================================== <=== stack pointer
という状態。
CALL の時点で呼び出されるクロージャに突入し、引数には frame pointer からのオフセットでアクセスする。
arg1 であれば frame pointer + 1。
RETURN は簡単で、FRAME で積んだものを3つ元に戻して復帰。
let フレーム
関連する命令は、LET_FRAME, ENTER, LEAVE。
もうほとんど手続き呼び出しフレームと一緒。
LET_FRAME は以下の順で以下のものを push する。
- current closure ポインタ(現在呼び出されているクロージャのポインタ)
- frame ポインタ
このとき frame ポインタはまだ更新されていない。
ENTER 後は引数が積まれる、(let ([arg0 a] [arg1 b]) ...) なら arg0, arg1 の順に積まれる。
ENTER では frame ポインタを引数を積む直前に設定する。
つまりこの時点でスタックは
======================================== curren closure ======================================== prev frame pointer ======================================== <=== frame pointer arg0 ======================================== arg1 ======================================== <=== stack pointer
という状態。
ちなみにこの frame ポインタ設定のために、今回 ENTER に引数の個数オペランドを追加した。
ENTER の時点で呼び出される(display)クロージャに突入し、引数には frame pointer からのオフセットでアクセスする。
arg1 であれば frame pointer + 1。
LEAVE は簡単で、LET_FRAME で積んだものを2つ元に戻して復帰。
二つの frame に共通すること
エラーが起きたときに、その時点の frame ポインタから固定のオフセットで
- 1つ前の frame ポインタ
- 1つ前の closure ポインタ
にアクセスできるように配置の仕方を似せてあります。