この実験では,コンパイラの実装言語としてOCaml言語を使用する.コンパイラの実装にあたっては必ずしもこの資料に書かれた手順に従う必要はなく,任意のMiniMLプログラムを言語仕様どおりに振る舞うARMの実行ファイルへと変換できるのであれば,どのように設計しても構わない.

この資料の手順に従うのであれば,実験ホームページから入手できるOCamlで書かれた配布コード一式を基にして実装するとよい.

以下,配布コードの使い方やソースファイルの構成について簡単に説明する.この資料に書かれている手順に従わずにコンパイラを実装する場合であっても,自分でつくるコンパイラのユーザインタフェースがどうなっているべきかを知るために以下の説明に目を通し,また,一度は配布コードをビルド・実行して動作確認するようにして欲しい.

配布コード一式をダウンロードし,展開したディレクトリでmakeコマンドを実行するとminimilcという名前の実行ファイルが出来上がる.

まずは,出来上がった実行ファイルを引数やオプションをつけずに起動してみる.実験3で作成したインタプリタ同様,ユーザの入力を促すプロンプトが表示されるはずである.試しに簡単な式1+2を入力してみよう.すると:

$ ./minimlc
# 1+2;;
        .text
        .align 2
        .global _toplevel
_toplevel:
        mov      a1, #1
_toplevel_ret:
        bx       lr
#

のようなARMアセンブリコードが標準出力に出力される.このアセンブリコードは,を計算しないことに注意して欲しい.配布コードのコンパイラは(当然のことながら)未完成であるため,どんな入力を与えようとも式1を計算するだけのコードが出力されるようになっている.以下では,「完成した暁にはこのように動作すべきである」というminimlcの振舞いについて説明する.ここでの説明どおりに動作するコンパイラを実現することが,この実験で課せられた課題である.

まず,アセンブリコードを標準出力に出力するのではなく,ファイルに書き出したい場合には,minimlcコマンドに「-o ファイル名」オプションをつけて指定する.また,-Oオプションを指定すると,コンパイラは最適化フェーズを実行する(MiniMLコンパイラの各フェーズの説明については次節を参照).たとえば,次のように(完成版の)minimlcを起動し,半径10の円の面積を 計算するプログラム:

$ ./minimlc -O -o tmp.s
# let square = fun x -> x * x in
  let radius = 10 in
  3 * radius * radius;;
#

を入力すると,以下のARMアセンブリコードがファイルtmp.sに出力される.なお,図中のARMアセンブリコードの読み方はARM命令セットで説明するので,この時点では読めなくてもかまわない.

        .text
        .align 2
        .global _b__f0
_b__f0:
        str     fp, [sp, #-4]
        str     lr, [sp, #-8]
        sub     fp, sp, #4
        sub     sp, sp, #16
        mul     v1, a2, a2
        mov     a1, v1
        b       _b__f0_ret
_b__f0_ret:
        add     sp, fp, #4
        ldr     lr, [fp, #-4]
        ldr     fp, [fp, #0]
        bx      lr
        .align 2
        .global _toplevel
_toplevel:
        str     fp, [sp, #-4]
        str     lr, [sp, #-8]
        sub     fp, sp, #4
        sub     sp, sp, #16
        str     a1, [sp, #0]
        str     a2, [sp, #4]
        mov     a1, #1
        bl      mymalloc
        mov     ip, a1
        ldr     a1, [sp, #0]
        ldr     a2, [sp, #4]
        ldr     a3, =_b__f0
        str     a3, [ip, #0]
        mov     v1, ip
        mov     v2, #10
        mov     a3, #3
        mul     v3, a3, v2
        mul     v4, v3, v2
        mov     a1, v4
        b       _toplevel_ret
_toplevel_ret:
        add     sp, fp, #4
        ldr     lr, [fp, #-4]
        ldr     fp, [fp, #0]
        bx      lr

minimlcに(オプションではなく)コマンドライン引数として入力ファイルを指定すると,プロンプトを表示せずにバッチコンパイルを行う.たとえば:

$ ./minimlc -o example/fib.s example/fib.ml

と実行すると,example/fib.mlファイル中のMiniMLプログラムをコンパイルした結果がexample/fib.sファイルに書き出される.

minimlc-vオプションを指定すると,コンパイラの各フェーズ間の入出力となる中間表現が標準出力に表示される.

$ ./minimlc -v -O -o tmp.s
# 1+2;;
(* [Normal form] *)
1 + 2;;

(* [Closure] *)
1 + 2;;

(* [Flat] *)
let rec _toplevel () =
  1 + 2

(* [VM code] *)
proc _toplevel(1) =
  add	t0, 1, 2
  ret	t0

(* [Reg code] *)
proc _toplevel(0) =
  add	r0, 1, 2
  ret	r0

#

-vオプション指定時に各中間表現を表示する処理は,未完成の配布コード中にすでに含まれているため,各フェーズの変換処理を完成させるだけで,上記のように表示されるようになるはずである.(先述のとおり,配布時にはどんな入力に対しても式1に相当する中間表現が表示されるだけである.)

minimlcコマンドは,上記以外に-Gオプションも受け付けるが,これについては 最適化で説明する.