この実験では,コンパイラの実装言語としてOCaml言語を使用する.コンパイラの実装にあたっては必ずしもこの資料に書かれた手順に従う必要はなく,任意のMiniMLプログラムを言語仕様どおりに振る舞うARMの実行ファイルへと変換できるのであれば,どのように設計しても構わない.
この資料の手順に従うのであれば,実験ホームページから入手できるOCamlで書かれた配布コード一式を基にして実装するとよい.
以下,配布コードの使い方やソースファイルの構成について簡単に説明する.この資料に書かれている手順に従わずにコンパイラを実装する場合であっても,自分でつくるコンパイラのユーザインタフェースがどうなっているべきかを知るために以下の説明に目を通し,また,一度は配布コードをビルド・実行して動作確認するようにして欲しい.
配布コード一式をダウンロードし,展開したディレクトリでmake
コマンドを実行するとminimilc
という名前の実行ファイルが出来上がる.
make clean; make
」を実行してみると上手く行くかも知れない.まずは,出来上がった実行ファイルを引数やオプションをつけずに起動してみる.実験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
オプションも受け付けるが,これについては
最適化で説明する.