以前に.NETの中間コード(MSIL)用のTinyBasicコンパイラとか、Javaの中間コード(bytecode)用のTinyBasicコンパイラとか作っており、次はLLVM用をやってみたいと考えている。
→ Javaバイトコードを生成するTiny BASICコンパイラを作ってみた
→ CIL(MSIL)中間コードを生成するTiny BASICコンパイラを作ってみた
きつねさんでもわかるLLVMという本を参考にして、LLVMの中間コードアセンブラを試してみた。
きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~(Amazon)
この本では、Ubuntu上でLLVMをソースからビルドしてインストールしていたのだけど、Mac上で作業するためにMac OS XでLLVMをインストールした。
→ Mac OS X MavericksにHomebrewでLLVMをインストール
LLVMが動かせたので、まず最初に、参考書などに載っているHello worldというサンプルコードを試してみた。
@HW = internal constant [14 x i8] c"Hello world!\0A\00"
declare i32 @puts(i8*)
define i32 @main() {
%cast_hw = getelementptr [14 x i8]* @HW, i32 0, i32 0
call i32 @puts(i8* %cast_hw)
ret i32 0
}
上記を、hello.llという名前で保存し、
% llvm-as hello.ll
というコマンドでビルドすると、LLVMの中間コードのbitcode出力が得られる。
lliを使って実行して、動作を確認することができた。
で、その次は、a=2; b=3; printf(“%d”,a); printf(“%d”,b); a=a+b; printf(“%d”,a);というような処理を書いてみた。
define i32 @main() {
%a= alloca i32
%b= alloca i32
store i32 2 , i32* %a
store i32 3 , i32* %b
%cast_msg = getelementptr [4 x i8]* @MSG, i32 0, i32 0
%tmp1=load i32* %a
%tmp2=load i32* %b
call i32 (i8*, ...)* @printf(i8* %cast_msg, i32 %tmp1)
call i32 (i8*, ...)* @printf(i8* %cast_msg, i32 %tmp2)
%tmp3= add i32 %tmp1,%tmp2
store i32 %tmp3, i32* %a
%tmp4=load i32* %a
call i32 (i8*, ...)* @printf(i8* %cast_msg, i32 %tmp4)
ret i32 0
}
declare i32 @printf(i8*, ...)
@MSG = internal constant [4 x i8] c"%d\0A\00"
静的単一代入のため、普通に%aや%bという変数を整数型にして計算することができないため、ポインタ変数を使ってメモリ確保をして一時変数に取り出しながら処理をしている。
そのためtmp1,tmp2,tmp3,..とかSSA風の変数をたくさん使い捨てで使っている。
こういうやりかたで良いのかちょっと自信がないが、なんとなくTinyBasicくらいならいけそうな気がしてきた。
—
追記
時間が空いてしまったが、ようやくLLVM用のTinyBasicを作った。
→ LLVM中間コードを生成するTiny BASICコンパイラを作ってみた