llvm-py を使ってみた。
llvm-pyはドキュメントがめちゃくちゃよく書けているのはいいんだけど、バインディングとしての完成度は…普通かな。
0 から 9 までの値をそれぞれ 2 倍したものを表示するサンプルを書いてみた。実は with を初めて使った。
basic block (BB) のセマンティクスとか、定義済み外部関数で、かつ可変引数である printf の呼び出し方とかが参考になるといいなと思う。
文字列定数の扱いはひょっとしたら間違ってるかもしれない。ドキュメントいろいろ漁ったけどよくわからなかったので。
出力結果は次のようになるはず。printf 呼び出しがインライン化されたりしているのは最適化パス (PASS_FUNCTION_INLINING) のおかげ。いろいろパスを足したり除いたりして、どう最適化が効いてくるのかを見るのもおもしろい。
BEFORE: define void @loop() { entry: %0 = alloca i32 ; <i32*> [#uses=5] store i32 0, i32* %0 br label %loop loop: ; preds = %if_0, %entry %1 = load i32* %0 ; <i32> [#uses=1] %2 = icmp ult i32 %1, 10 ; <i1> [#uses=1] br i1 %2, label %if_0, label %__break__if_0 br label %if_0 __break__loop: ; preds = %__break__if_0 ret void if_0: ; preds = %loop, %loop %3 = load i32* %0 ; <i32> [#uses=1] call void @plus1(i32 %3) %4 = load i32* %0 ; <i32> [#uses=1] %5 = add i32 %4, 1 ; <i32> [#uses=1] store i32 %5, i32* %0 br label %loop br label %__break__if_0 __break__if_0: ; preds = %if_0, %loop br label %__break__loop } AFTER: define void @loop() { entry: %0 = alloca i32, align 4 ; <i32*> [#uses=5] store i32 0, i32* %0 br label %loop loop: ; preds = %if_0, %entry %1 = load i32* %0 ; <i32> [#uses=1] %2 = icmp ult i32 %1, 10 ; <i1> [#uses=1] br i1 %2, label %if_0, label %__break__if_0 br label %if_0 if_0: ; preds = %loop, %loop %3 = load i32* %0 ; <i32> [#uses=1] %4 = shl i32 %3, 1 ; <i32> [#uses=1] call void (i8*, ...)* @printf(i8* getelementptr ([8 x i8]* @0, i32 0, i32 0), i8* getelementptr ([5 x i8]* @1, i32 0, i32 0), i32 %4) %5 = load i32* %0 ; <i32> [#uses=1] %6 = add i32 %5, 1 ; <i32> [#uses=1] store i32 %6, i32* %0 br label %loop br label %__break__if_0 __break__if_0: ; preds = %if_0, %loop br label %__break__loop __break__loop: ; preds = %__break__if_0 ret void } test: 0 test: 2 test: 4 test: 6 test: 8 test: 10 test: 12 test: 14 test: 16 test: 18
また、末尾再帰を使ったバージョンも書いてみた。
こっちの方が構造的にシンプルになっている。