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 の中身がないので、なにもしないでおわりです!
これでは悲しいので、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 をたたきまくれば、どんなバイナリでもつくれてしまいます。
無限の可能性!!