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また、末尾再帰を使ったバージョンも書いてみた。
こっちの方が構造的にシンプルになっている。