Tutorial 続き

Tutorial の例には main が無いため、せっかく生成した llvm のコードの断片を実行できない。
main を足した版。※私のC++ 知識は入門以下レベルなので、決して真似しないこと。

コンパイルして実行ファイル tut2 を作り、

c++ -g tut2.cpp `/usr/local/bin/llvm-config --cxxflags --ldflags --libs core` -o tut2

実行すると llvm ir を出力する。以下は tut2 10 3 7 を実行したもの。llvm の関数 mul_add に 10, 3, 7 を渡し、結果を返す。この例では 10 * 3 + 7 = 37 を返す。パイプで llvm-as, lli をつなぐとステータスとして 37 が確認できる。

./tut2 10 3 7 | llvm-as | lli
echo $? 
37

tut2 が出力するllvm のコード。

; ModuleID = 'tut2'

define i32 @mul_add(i32 %x, i32 %y, i32 %z) {
entry:
%tmp = mul i32 %x, %y ; [#uses=1]
%tmp2 = add i32 %tmp, %z ; [#uses=1]
ret i32 %tmp2
}

define i32 @main() {
entry:
%tmp = call i32 @mul_add(i32 10, i32 3, i32 7) ; [#uses=1]
ret i32 %tmp
}

ちょっとllvm を思い出すために脱線。llvm の最適化(opt)を掛けて disassemble する(llvm-dis)とこのくらいの簡単なコードは完全に定数になってしまうのだった。

./tut2 10 3 7 | llvm-as | opt -std-compile-opts | llvm-dis
; ModuleID = '<stdin>'

define i32 @mul_add(i32 %x, i32 %y, i32 %z) nounwind readnone {
entry:
	%tmp = mul i32 %y, %x		; <i32> [#uses=1]
	%tmp2 = add i32 %tmp, %z		; <i32> [#uses=1]
	ret i32 %tmp2
}

define i32 @main() nounwind readnone {
entry:
	ret i32 37
}

恐らくこの最適化も、わざわざ llvm ir を出力せず、tut2.cpp の内部で実行できるはず。

tut2.cpp

#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/PassManager.h"
#include "llvm/CallingConv.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Assembly/PrintModulePass.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

Module* makeLLVMModule();
void addMain(Module* mod, int x, int y, int z);

int main(int argc, char**argv) {
  Module* Mod = makeLLVMModule();
  if (argc != 4) {
    cerr << "Usage: tut2 x y z\n";
    return 1;
  }
  addMain(Mod, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));

  verifyModule(*Mod, PrintMessageAction);

  PassManager PM;
  PM.add(createPrintModulePass(&outs()));
  PM.run(*Mod);

  delete Mod;
  return 0;
}

void addMain(Module* mod, int x, int y, int z) {
  Constant* cmain = mod->getOrInsertFunction("main", IntegerType::get(32), NULL);
  Function* main = cast<Function>(cmain);
  main->setCallingConv(CallingConv::C);
  BasicBlock* block = BasicBlock::Create("entry", main);
  IRBuilder<> builder(block);

  std::vector<Value*> args1;
  args1.push_back(ConstantInt::get(Type::Int32Ty, x));
  args1.push_back(ConstantInt::get(Type::Int32Ty, y));
  args1.push_back(ConstantInt::get(Type::Int32Ty, z));
  // get mul_add fuction
  Constant* c = mod->getOrInsertFunction("mul_add",
                                         IntegerType::get(32),
                                         IntegerType::get(32),
                                         IntegerType::get(32),
                                         IntegerType::get(32),
                                         NULL);
  Function* mul_add = cast<Function>(c);

  Value* ret = builder.CreateCall(mul_add, args1.begin(), args1.end(), "tmp");

  builder.CreateRet(ret);
}

Module* makeLLVMModule() {
  Module* mod = new Module("tut2");
  Constant* c = mod->getOrInsertFunction("mul_add",
                                         /*ret type*/
                                         IntegerType::get(32),
                                         /*args*/
                                         IntegerType::get(32),
                                         IntegerType::get(32),
                                         IntegerType::get(32),
                                         /*varargs terminated with null*/
                                         NULL);
  Function* mul_add = cast<Function>(c);
  mul_add->setCallingConv(CallingConv::C);
  Function::arg_iterator args = mul_add->arg_begin();
  Value* x = args++;
  x->setName("x");
  Value* y = args++;
  y->setName("y");
  Value* z = args++;
  z->setName("z");

  BasicBlock* block = BasicBlock::Create("entry", mul_add);
  IRBuilder<> builder(block);

  Value* tmp = builder.CreateBinOp(Instruction::Mul, x, y, "tmp");
  Value* tmp2 = builder.CreateBinOp(Instruction::Add, tmp, z, "tmp2");
  builder.CreateRet(tmp2);

  return mod;
}