プログラミング言語を作る。第7回:LLVM入門
LLVM を用いて FizzBuzz を書きました。色々と躓いた点があったので、それを記述しておきます。
まずはプログラム本体から
@s1 = constant [10 x i8] c"FizzBuzz\0A\00" @s2 = constant [6 x i8] c"Buzz\0A\00" @s3 = constant [6 x i8] c"Fizz\0A\00" @s4 = constant [4 x i8] c"%d\0A\00" declare i32 @printf(i8*, ...) define i32 @main(){ %i = alloca i32, align 4 store i32 1, i32* %i br label %L1 L1: %v1 = load i32* %i, align 4 %v2 = icmp sle i32 %v1, 100 br i1 %v2, label %L2, label %L3 L2: %v3 = srem i32 %v1, 15 %v4 = icmp eq i32 %v3, 0 br i1 %v4, label %L4, label %L5 L4: %v5 = call i32 (i8*, ...)* @printf(i8* getelementptr ([10 x i8]* @s1, i32 0, i32 0)) br label %L6 L5: %v7 = srem i32 %v1, 5 %v8 = icmp eq i32 %v7, 0 br i1 %v8, label %L7, label %L8 L7: %v9 = call i32 (i8*, ...)* @printf(i8* getelementptr ([6 x i8]* @s2, i32 0, i32 0)) br label %L6 L8: %v10 = srem i32 %v1, 3 %v11 = icmp eq i32 %v10, 0 br i1 %v11, label %L9, label %L10 L9: %v12 = call i32 (i8*, ...)* @printf(i8* getelementptr ([6 x i8]* @s3, i32 0, i32 0)) br label %L6 L10: %v13 = call i32 (i8*, ...)* @printf(i8* getelementptr ([4 x i8]* @s4, i32 0, i32 0), i32 %v1) br label %L6 L6: %v6 = add i32 %v1, 1 store i32 %v6, i32* %i br label %L1 L3: ret i32 0 }
特に二種類の要点を挙げます。
ラベルはそれ以前で使われている必要がある。
例えば、次のような命令列は、ラベルの部分で存在しない operator と言われます。
L2: %i = alloca i32, align 4 store i32 1, i32* %i %b = icmp eq i32 %i, 0 br i1 %b, label %L1, label %L2 L1: ret i32 0
L2 というラベルが br で初めて使われる前に来ているからです。上のプログラムの for ループを書く部分で、 br label %L1 という記述があり、直後にジャンプする不自然な挙動をしていますが、そういう理由で付けざるを得ませんでした。
単なる数値の変数は、後ろにインクリメントされたラベルが付与される。
上のプログラムでは %v1 のように v と数字で変数を表現していますが、最初は %1 のように書いていました。しかし、例えば
%1 = alloca i32 ret i32 0
のようなプログラムは、
%1 = alloca i32 2: ret i32 0
というように勝手にラベルの付与がなされるようです。%数字で変数を表現すると特殊な処理になっているのですね。これで前回の疑問(コメントアウトしたラベルがなぜか有効)も理由がわかりました。
LLVM をちょっとさわってみて、なかなか制約の多いように感じました。これでは、独自の中間コードを記述してシミュレータを書くのもありかもしれません。