アセンブリ生成(emit.ml)

 

ついに最終段階のアセンブリ生成となりました。もっともややこしいレジスタ割り当てがすでに終わっているので、特に難しいことはなく、SparcAsm.tを本当のSPARCアセンブリとして出力するだけです。

 

ただし、仮想命令はちゃんと実装する必要があります。条件分岐は比較とブランチで実装します。SaveRestoreは、すでにセーブした変数の集合stacksetと、変数のスタックにおける位置のリストstackmapとを計算しつつ、ストアとロードで実装します。関数呼び出しでは、レジスタの順に引数を並べ替える処理Emit.shuffleが少し面倒ですが、それ以外は簡単です。

 

一点だけ自明でない重要な部分として、末尾呼び出し最適化があります。これは「もう後にすることがなく、戻ってこない」ような関数呼び出し(末尾呼び出し)を、Callではなく、ただのジャンプ命令で実装するという処理です。これにより、たとえば冒頭のgcdのような再帰関数を、ただのループとまったく同様に実装することができます。このために、命令列のアセンブリ生成をする関数Emit.gや、各命令のアセンブリ生成をする関数Emit.g'では、末尾かどうかを表すデータ型Emit.destも引数として受け取ります。末尾Tailだったら、他の関数をジャンプ命令で末尾呼び出しするか、さもなくば計算結果を第一レジスタにセットし、SPARCret命令で関数を終了します。末尾でないNonTail(x)だったら、計算結果をxセットします。

 

最後に、ヒープと関数呼び出しスタックを確保するメインルーチンstub.cと、テストプログラムの実行に必要な外部関数libmincaml.Sを用意すれば、MinCamlコンパイラの完成です!