llvm で Hello wolrd!! 〜llvm入門 その2〜
http://www.ibm.com/developerworks/jp/opensource/library/os-createcompilerllvm1/
最初なのでとりあえず↑の記事を基本なぞってますが、わかりやすく解説をいれています。
llvm であそぶには、まあいろいろな方法がありますが、わかりやすく大きくわけると以下の4ステップです。
- llvm IR の動的生成
- llvm IRの最適化
- llvm IRの JIT コンパイル
- llvm IRのネイティブコードへの変換
それぞれのフェーズごとに分離して動作させることができるので、創りたいところだけつくればいいのです。
とりあえず基本となる llvm IR の動的生成をおこなってみます。
とりあえずなにもしない main 関数をつくりましょう。
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { // コンテキストの生成 llvm::LLVMContext& context = llvm::getGlobalContext(); // モジュールの生成 llvm::Module *module = new llvm::Module("top", context); // アセンブリビルダの生成 llvm::IRBuilder<> builder(context); // main関数をつくる(中身はからっぽ) llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getInt32Ty(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); // エントリポイントを追加する llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc); builder.SetInsertPoint(entry); // void をかえす。(return; とおなじですね) builder.CreateRetVoid(); // できたアセンブリを標準出力にだす。 module->dump( ); }
これをコンパイルする。
clang++ -g tut1.cc `llvm-config-3.0 --cppflags --ldflags --libs` -ldl -lpthread
実行結果は以下のとおり。まあなにもない!
$ ./a.out ; ModuleID = 'top' define i32 @main() { entrypoint: ret void }
これをそのまま lli で実行すると、、
$ ./tut1 &> foo.s $ lli foo.s $
main の中身がないので、なにもしないでおわりです!
main の中身を実装する
これでは悲しいので、Hello world! してみましょう。 puts 関数をとりだしてよんでいるのがシャレオツですね。llvm でなんか書いててもC言語の関数は簡単によべることがわかるのではないでしょうか。
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { // コンテキストの生成 llvm::LLVMContext& context = llvm::getGlobalContext(); // モジュールの生成 llvm::Module *module = new llvm::Module("top", context); // アセンブリビルダの生成 llvm::IRBuilder<> builder(context); // main関数をつくる(中身はからっぽ) llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getInt32Ty(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); // エントリポイントを追加する llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc); builder.SetInsertPoint(entry); // 文字列を定義する llvm::Value *helloWorld = builder.CreateGlobalStringPtr("hello world!\n"); // puts をとりだしてつかえるようにセットアップする。 std::vector<llvm::Type *> putsArgs; putsArgs.push_back(builder.getInt8Ty()->getPointerTo()); llvm::ArrayRef<llvm::Type*> argsRef(putsArgs); // puts の型 llvm::FunctionType *putsType = llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false); // puts 関数をいよいよとりだした! llvm::Constant *putsFunc = module->getOrInsertFunction("puts", putsType); // puts関数をよびだす builder.CreateCall(putsFunc, helloWorld); / return; builder.CreateRetVoid(); // できたアセンブリを標準出力にだす。 module->dump( ); }
これをコンパイルする。
clang++ -g tut2.cc `llvm-config-3.0 --cppflags --ldflags --libs` -ldl -lpthread
./a.out を実行すると以下の llvm アセンブリをえる。
; ModuleID = 'top' @0 = internal unnamed_addr constant [14 x i8] c"hello world!\0A\00" define i32 @main() { entrypoint: %0 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @0, i32 0, i32 0)) ret void } declare i32 @puts(i8*)
これを、実際に lli で実行すると、ちゃんと hello world が実行された! ヤッター!
~/dev/llvm-tutorial$ lli tut3.s hello world!
まとめ
llvm で、実行可能なバイナリになる IR を生成してみました。
あとはいろいろ IR を生成する API をたたきまくれば、どんなバイナリでもつくれてしまいます。
無限の可能性!!