SBCL のビルドプロセスの外観
Common Lisp の実装の1つである SBCL (Steel Bank Common Lisp) のビルドプロセスは、Allegro Common Lisp や LispWorks といった、イメージベースでそれ自身の上でのみビルド可能な Common Lisp 実装とは異なり、SBCL 以外の Common Lisp 実装の上でもビルドできます。それを実現するために、SBCL のビルドプロセスでは、様々なテクニックが使われています。
このエントリでは、SBCL のビルドプロセスを順を追って説明します。
host-1 ビルドステージ
まず、ホスト上にクロスコンパイラを読み込みます。SBCL のソースコードをホストコンパイラでコンパイルしロードすることで、ホスト上にクロスコンパイラを展開します。
続いて、後のステージのための前処理として、sb!fasl:genesis の読み込みと、C ヘッダファイルの出力を行います。sb!fasl:genesis は、genesis-2 ビルドステージで、'cold core' を生成するために使われます。C ヘッダファイルは、続く target-1 ビルドステージで、SBCL の実行形式を構築するのに使われます。
target-1 ビルドステージ
target-1 ビルドステージでは、大きく2つの処理が行われます。1つは、SBCL 実行形式の構築です。host-1 ビルドステージで生成された C ヘッダファイルと、その他の C ソースファイルおよびアセンブリファイルから、SBCL の実行形式を構築します。ただし、この実行形式は、この時点ではまだ動作しません。OS サービスへのインターフェイスやガベージコレクタを提供するものの、次ステージ以降で生成されるターゲットメモリイメージを含まないためです。
もう1つの処理は、ターゲットの一部として使用される Lisp ソースコードの生成です。この Lisp ソースコードは、小さな C プログラムをコンパイルおよび実行することで生成され、システム依存の定数と型に関する情報を提供します。
host-2 ビルドステージ
このステージでは、SBCL 用のオブジェクトファイル (FASL ファイル) が生成されます。オブジェクトファイルは、クロスコンパイラ版の compile-file を用いて、SBCL ソースコードを再度コンパイルすることで得られます。このとき、SBCL ソースコードの他に、host-1 ビルドステージで生成した Lisp ソースコードを併せて用いることで、クロスコンパイラ上の定数定義を、システム環境に応じたものに更新します。
genesis-2 ビルドステージ
続いて、genesis-2 ビルドステージに入ります。ここでは、host-1 で読み込んだ sb!vm:genesis を用いて、FASL ファイルのロードとメモリイメージの保存をシミュレートします。sb!vm:genesis が必要となる理由は、ホストコンパイラの load 関数では、SBCL の FASL 形式をロードできず、また、SBCL のターゲットイメージがまだ存在しないことからその load 関数を使うこともできないためです。
ここで行う処理は、次のように進みます。まず、特定の順序で擬似的に FASL ファイルをロードし、メモリイメージを展開します。次に、ターゲットのメモリ領域をバイト配列とみなし、FASL ファイルをロードしたときに起こるであろう処理をシミュレートします。この方法で実行できない処理は、ターゲットイメージの初期化関数が実行されるまで遅延されます。すべての FASL ファイルがこの方法で処理された後、メモリ領域を SBCL 実行形式が期待する形式でファイルに保存します。これで 'cold core' が生成されました。
最後に、'cold core' をメモリイメージとして、SBCL 実行形式を起動します ('cold init')。遅延されていたトップレベル・フォームの処理などは、このタイミングで実行されます。'cold init'は、助けになる Lisp デバッガが存在しないことから最もデバッグしづらいため、現在の SBCL のビルドプロセスにおいて、もっとも脆弱な箇所といえます。
target-2 ビルドステージ
SBCL のビルドプロセスの最終ステージが、target-2 ビルドステージです。まず、パッケージを最終的な名前に変更し、sb! プレフィックスを、sb- プレフィックスとします。次に、SBCL に特殊化されたバージョンの Portable Common Loops (PCL) をコンパイルし、ロードします。最後に、そうしてできた新しいメモリイメージを、output/sbcl.core として保存します。
これで、ビルドプロセスが完了しました。
まとめ
このエントリでは、SBCL のビルドプロセスを順を追って説明しました。SBCL のアプローチにもメリット・デメリットはありますが、このような仕組みでセルフ・ブートストラッピングを実現しているというのは、興味深いところです。
参考
SBCL - a Sanely-Bootstrappable Common Lisp
http://www.doc.gold.ac.uk/~mas01cr/papers/s32008/sbcl.pdf