tokuhirom's Blog

llvm で Hello wolrd!! 〜llvm入門 その2〜

http://www.ibm.com/developerworks/jp/opensource/library/os-createcompilerllvm1/
最初なのでとりあえず↑の記事を基本なぞってますが、わかりやすく解説をいれています。

llvm であそぶには、まあいろいろな方法がありますが、わかりやすく大きくわけると以下の4ステップです。

それぞれのフェーズごとに分離して動作させることができるので、創りたいところだけつくればいいのです。

とりあえず基本となる 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 をたたきまくれば、どんなバイナリでもつくれてしまいます。
無限の可能性!!