はじめに
「RubyでつくるRuby ゼロから学びなおすプログラミング言語入門」(ラムダノート, Amazon) という本を手を読んで非常に感銘を受けました。そこでも自分でもNode.jsでミニNode.jsインタープリタを作ってみました。なんとか達成して大きな自己満足を味わうことができました。
実はそのチャレンジにはもう一つのきっかけがあります。それは Turing Complete FM のポッドキャストを聴き始めたのことです。
このポッドキャストを聴きなら、そういえば随分昔に「自分で小さなコンパイラを作ること」に憧れていたことを思い出しました。小さなインタープリタを作れた今なら当時挫折したことができそうな気がして、今度はコンパイラ作りを目指します。
目指すもの
ミニインタープリタを作って分かったことは、適切に小さくした目標が重要だということです。今回目指すミニNode.jsコンパイラも、思い切って割り切った言語仕様にすることにしました。
- FizzBuzzと、フィボナッチ数列の計算ができるようする
- 仕様はミニNode.js (つまり、ミニRuby)を踏襲する
- ただし、配列やハッシュは除外する
- 型は32ビット符号付き整数のみサポート
- ローカル変数が使える。グーローバル変数は無し
- if - else, while が使える
- ユーザ定義関数が使える(再帰呼び出しもできるようにしたい)
- 組み込み関数として、整数の出力と固定文字列の出力ができるようにする
コンパイラなので型を導入しますが、1種類に割り切るというJavaScriptらしからぬ仕様にしました。これで型宣言も型推論も不要です(ドヤ顔)。
またコンパイラなので、最終的にはNode.js無しで実行できるバイナリを生成しなければなりません。が、そこは便利なツールを利用させてもらいます。
- 単独で実行できるバイナリを生成することを目指す
- バイナリ生成は、Node.jsではなくて別のツールを利用する
- ミニコンパイラでは、中間コードを生成する
- 自分自身をコンパイルすることは諦める(セフルホストはしない)
- ミニコンパイラ自体は、ミニNode.jsインタープリタで実行できることを目指す
前提環境
今回は私の環境である Mac OS X 10.12 Sierra を前提に、取り組みを進めます。
コード生成に使うツール
最終的にバイナリを生成するところは、他のツールに任せることにしました。これでかなりコンパイラを作るハードルが下がります。ツールの候補としては、次のものを考えました。
- Cコンパイラ (gcc or Clang)
- Goコンパイラ
- Rustコンパイラ
- LLVM
最初の3つの場合は、Node.jsから各言語のソースコードに変換することになります。コンパイラを作ったというと、ちょっと違う感じがします。
そこで以前から気になっていたLLVMを使うことにしました。つまり、 Node.jsでLLVMの中間表現(Internal Representation)を生成することになります。
LLVMの準備
LLVMとは
LLVMによると
LLVMプロジェクトは、モジュール化された再利用可能なコンパイラおよびツールチェーン技術の集まりです
とあります。もともとは Low Level Virtual Machine の略語として名付けられたらしいのですが、いまはLLVMが正式な名称とのことです。
最近の言語系ではよく利用さているらしく、例えばClang, Swift, Rustが代表例です。ASM.jsやWebAssemblyを生成するEmscriptenもLLVMを利用しているそうです。
ちなみにGoもLLVMを使っているのかと思っていたのですが、実際は違いました。コンパイラもライブラリも自前で作るのがGoの流儀なようです。
LLVMのインストール
LLVM は Clang と一緒に提供されているようです。(Download page) その中で、今回は次のツールを使います。
- lli ... LLVM IR(中間表現)やBitcode(バイトコード)を実行するツール
- llc ... 実行モジュールのバイナリを生成するツール
Mac OS X の場合、clangは /usr/bin/clang にインストールされていますが、このツール群は含まれていません。また Xcode をインストールした場合にも clang がインストールされますが、lli/llcといったツールは無いようです。
Clang/LLVMのインストールには次の方法があります。
- (1) ソースコードからビルドする
- (2) homebrewを使ってインストールする(Mac OS Xの場合)
- (3) ビルド済みのバイナリ(Pre-build)をダウンロードする
今回はなるべく簡単にすませたくて、3番目のビルド済みバイナリをダウンロードする方法をとりました。Mac OS Xで作業しているため、こちらのページから Clang for Mac をダウンロードします。2018年7月現在、v6.0.0のバイナリが最新のようです。
圧縮ファイルを解凍すると中に下記のフォルダーが現れます。
- bin
- share
- include
- lib
- libexec
一式をお好きな場所にコピーしてbinにパスを通してください。私の場合はパスを通す代わりに次のようにしました。
- ~/Application/clang/ 以下に各フォルダーをコピー
- bashのエイリアスを設定
- alias llc='~/Applications/clang/bin/llc'
- alias lli='~/Applications/clang/bin/lli'
次回は
つぎはLLVM IRの書き方を調べてみます。こちらのページを参考にします。