第11回 カーネル/VM 探検隊
アセンブリ言語のみで言語処理系を作った話(原題: ブートストラッピングで言語処理系を作った話)第11回 カーネル/VM探検隊中村 晃一
View Slide
本日の資料・コード•https://speakerdeck.com/nineties/bootstrap•https://github.com/nineties/amber
自己紹介• 中村晃一(@9_ties)• IoTデバイスを作っています• idein.jp• 課外活動• 圏論勉強会、パターン認識・機械学習勉強会などの講師
処理系作りが趣味でした• 学生実験でコンパイラ係に• O’CamlでminCamlコンパイラを作ったり• Haskellでも作りましたgithub.com/nineties/Choco• 大学院では最適化コンパイラの研究をしました• 特殊なプロセッサの為のコンパイラを書いたり• 型推論器を書いたり
自分の言語を作ろうと思いました• 言語名: 最初はrowl ⇒ 後にamber• ただ作るだけでなくその過程を楽しみたい• どうやったら楽しめるだろうか?
縛りを入れて作ろう• ルール1. 実装言語はアセンブリ言語のみ2. 既存ライブラリは使ってはならない3. コード生成ツールは使ってはならないlibcなどCなどの高級言語flex/bisonなど
作戦:ブートストラッピングアセンブリ言語でちょっと高級な言語1の処理系を書く言語1でもうちょっと高級な言語2の処理系を書く言語kでamberの処理系を書くamberでamberの処理系を書く 今ココ(暫らく放置中)
何の意味があるの?• 純粋に楽しみとして• 知識・技術・ノウハウを磨くのに役に立つかも• 効率の良い学習方法ではありませんが・・・• ツール群を作った先人への尊敬と感謝が芽生える
以下ダイジェストでお見せします
1.アセンブリで言語「rowl0」を実装
アセンブリより少し高級な言語を作ります• 言語名: rowl0• コンパイラ名: rlc
トークンの正規表現から
状態遷移図を書いて
状態遷移表にして
レキサを作ります
文法をBNFで書いたら
再帰降下法でパーサを作ります
コード生成はパースと同時にやりました• この時点でメモリ管理を実装するのは面倒⇒ 構文木作るの面倒コード生成
最初の言語「rowl0」が完成!• 変数管理が出来ない• 関数引数はp0,p1,p2,...• ローカル変数はallocate(n)でスタックメモリを確保しx0,x1,x2,...で参照
2.「rowl0」でLISP系言語「rowl-core」を実装
一旦LISPを作る事にしました• 言語名: rowl-core• インタプリタ名: rlci• 実装が容易• メタプログラミングによる生産性向上
さっきと同じ感じでレキサ・パーサを作る
アセンブリよりかなり記述が楽
evalも作っていきます。
メモリ管理はまだしません• mmapしか使えないので負担が大きい1. 不要になったメモリは回収しない2. 足りなくなったら新たに確保3. 動かし続けるといつか死ぬ• 次の世代のコンパイルだけ出来ればあと不要なので良しとしたmalloc, free
LISP系言語「rowl-core」が完成!• lambdaやmapが使えたり• マクロが使える!
3.「rowl-core」で「VM記述言語」を作る
次の世代では仮想マシンを導入します。• その為の「VM記述用言語」を作成。• LISPの強みを生かして、言語内DSLとして作成• レキサ・パーサの実装は不要!
こんな感じでVM用コンパイラを書きます
今回は高階関数とかも使えます• 生産性がぐっと向上!
4.「VM記述言語」で仮想マシン「rlvm」を実装
言語内DSLでVMのコードを書いていきます
ガベコレもようやく実装します• コピーGCにしました
組み込み関数も作っていきます
ここでメタプログラミングの活用例を紹介します• 以下は、VMの命令セットのテーブル
命令テーブルから色々自動生成します• 命令の追加・削除を自動的に反映• LISPだとこういう仕組み作るのが楽な気がするvm_instructionsVMのevalループリンカディスアセンブラアセンブラamber内部で使うアセンブラ
命令セットを作っていきます
浮動小数点数演算や
多倍長整数演算なども用意しました
例外機構なども作ります
継続も作ってみました
仮想マシン「rlvm」が完成!• 186命令• スタックマシン• コピーGC• 例外機構• shift/reset限定継続• 浮動小数点数演算、多倍長整数演算
5.VM用ツールチェインを作る
VMは出来たがプログラミング環境がない• VM上のツールチェインを作る• VM用中級言語「rowl1」• アセンブラ• コンパイラ• リンカ• ディスアセンブラ
言語・アセンブラ・コンパイラを作ります• これらは次世代で捨てるので「rowl-core」の言語内DSLにします
リンカ、ディスアセンブラを作ります• 「rowl1」で記述し、これら自身もVM上で動くように実装• リンカはメモリを食うのでGCのある環境でやりたかった
ディスアセンブラの出力はこんな感じ
VM上でプログラミングが可能に!• 分割コンパイルが出来る• デバッグもしやすい• メモリも潤沢に使える• 例外とかも使える• 普通の事が出来るようになっただけ・・・でも達成感がとても大きい!
6.「rowl1」で「amber」を実装
いよいよamberの実装に入ります• 動的スクリプティング言語として作る• インスタンスベースオブジェクト指向• 先ほど作った「rlvm」上で動作
アセンブラを作ります• さっき作ったアセンブラはプログラム実行前にコンパイル• これは、プログラム実行時にその場でコンパイル• アドレスはバックパッチ
オブジェクトシステムを作ります• スロット、メッセージ、 ペアレントへの委譲
オブジェクトシステム上に中核機能を作っていきます• 動的パターンマッチングエンジンと• 部分関数関数の融合メカニズム
この上にコンパイラを作ります• コンパイラ自体を通常のオブジェクトとして実装VMオブジェクトシステムパターンマッチングエンジンコンパイラamberのコアシステム構文木のマッチングリソース管理
クロージャ変換も実装しました
この上にパーサを作ります• 実行時にパーサをコンパイル• 各パーサは通常のオブジェクト(クロージャ)VMオブジェクトシステムパターンマッチングエンジンコンパイラamberのコアシステムコンパイルパーサ
とてもシンプルな文法1. リテラルは式である2. hがシンボル、e1,..,en (n>=0) が式の時h{e1, ..., en}も式である3. 以上で定まるもののみがamberの式である• これと、あるトリックを後で組み合わせる
Packrat法で実装します• 後のトリックのため• 従ってレキサ不要
浮動小数点数のエンコード・デコードは苦労します• libcが無いので自分で作ります• 先ほど作った多倍長整数演算が必要になります“3.14” 0x40091eb851eb851fstortod, sprintf
amberが完成!• 動的スクリプティング言語• VM上で動作• インスタンスベースオブジェクト指向• 動的パターンマッチングエンジンと部分関数融合が中核機能• レキシカルクロージャ• すごく高級になってきた!
7. 「amber」の標準ライブラリを作る
amberのテーマは自己拡張性• amberの文法は標準ライブラリで定義されている• amber/lib/syntax/parse.ab• 起動時に自身の文法を構築する
起動時はリテラルとh{e1,..,en}という文法だけ
amberの文法を定義する為の文法を定義する
今定義された文法でamberの文法を定義する
続いてマクロシステムを作る
マクロで更に文法を増強
リッチな構文が使えるようになりました
オブジェクトシステムも増強します
継承などが使えるようになりました
ここで開発停止です• 更新予定はありません• 以下でインタプリタが起動(Linuxのみ)• makeのログで以上のブートストラッピングの過程を見てみましょう% git clone https://github.com/nineties/amber.git% cd amber% make; sudo make install% amber
まとめrowl0rlc rlcirowl-coreasVM記述言語rlvmrowl1リンカディスアセンブラコンパイラコンパイラamberインタプリタ実装 実装実行言語ツール• 大分高級な所まで到達出来たので満足