今回その中のフロントエンド用のチュートリアル Kaleidoscopeを研究室の輪講としてやることになった。
なおAgdaの本をやっていたのはもう終わった。早かった。
LLVMのチュートリアルのページを見るとOCaml版とC++版がある。
しかしLLVMのリポジトリを見たら分かるとおり、OCaml版は本当に長く放置されていて、APIは古すぎてちゃんと新しいのを探さないといけないし、camlp4だし、ところどころ引っかかるみたいだし、そもそもObjective Camlだし...(頑張った方もいらしたようだ)
一方C++は2018年に何かで使ったらしく、修正が入っている。これはありがたい。
ソースコードは全て公開されているので、輪講としては若干気になるものの、絶対に環境に依存するから、事前に動かせるのがありがたい。
と思ったけどそれでも苦労するんだなぁ...
ビルドで困った人の助けになればと書いておく。
Ubuntu編
こちらは二転三転するのだが、結論を言うとリンカーにオプションを渡さないといけない。
aptでLLVMを入れてソースコードを持ってくると簡単にコンパイルできる。さすがUbuntu。優秀。
さくっとMakefile書いて、テスト用に(というにはおこがましいが)チュートリアルに書かれている例題を持ってきて、実行。そしてコケる。
Symbols not found - [ printd ]
そりゃないよ〜
Chapter 4から、JITを使ってシンボル解決をして外部関数を呼び出すということをするのだが、よくある数学関数(sinやcos)は普通に呼べるのに、処理系のソースコード内で定義している関数(putchardとかprintdとかいう名前)が呼べない。
チュートリアルでは何事もなかったように呼べている。いやいや呼べないんだけど。シンボル見つからないって文句言われるんだけど。
最初の解決策
どうやら共有ライブラリの関数は呼べているようなので、呼びたい関数を共有ライブラリ(.so)にして実行時にロードしてもらえばどうか、と試したところうまくいった。
うまくいったけど実行が面倒なんですがこれ...(実行時にいちいち LD_LIBRARY_PATH=. ./toy みたいに書いている)
別の解決策
JITのバグを疑っていて考えなかったけれど、そもそもこの関数シンボルテーブルにあるの?と思い立ってobjdumpで見てみる。
...前にオプションで二つのシンボルテーブルがあることを知った。
- -tで見られるシンボルテーブル。聞き覚え有り。
- -Tで見られる動的シンボルテーブル。聞き覚え無し。
人に聞いたらELF(linuxでよく使うバイナリフォーマット)にはあるらしい。動的シンボルテーブルの内容は動的リンク時などに使うテーブルなのだとか。
で、どうやらLLVMのJITがこっちを見ているようだ。printdやらputchardはシンボルテーブルにはあったけど動的シンボルテーブルにはなかった。なるほど動的リンクとJITだから確かに結びつく。
どうせリンカーに何かオプションがあるに違いないと思って調べたところ、--export-dynamicというフラグを見つけたので、clang++越しに渡すことにした。
clang++ -Xlinker --export-dynamic -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core mcjit native` -O3 -o toy
みたいにコンパイルする。
最終的にこれで無事動いた。
Mac編
端的に言うとllvm-configが出す情報がなんか不完全らしく、--libsの先をallにしたら足りた。あとリンカーのバージョンを下げるオプション-mlinker-version=450もつけたら動いた。
これ学生が見つけた話だからここに詳細を書いても仕方ない気がする。
こっちのビルド時のコマンドはこんな感じ。
clang++ -mlinker-version=450 -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -O3 -o toy
Ubuntuの時に使ったオプションは多分使えない。
あとlibsの指定でallを書くのはUbuntuでも有効。記述量減るので使った。頑張れclang。
なんとか無事輪講を開始できそうである。