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

また、末尾再帰を使ったバージョンも書いてみた。

こっちの方が構造的にシンプルになっている。