分岐、構造体、配列に関するLLVM C++ APIについて。
※LLVMのバージョンは10.0.0
目次
分岐
LLVMにはif else文そのものや、do、while、forもない。
br
で再現する必要がある。br
は無条件でもジャンプできるし、条件にあわせて行先を指定することもできる。
// 無条件分岐 builder.CreateBr(destBlock); // 条件付き分岐 builder.CreateCondBr(i1Value, thenBlock, elseBlock);
builder
はllvm::IRBuilder
型の変数。説明はこちら
CreateCondBr()
の第一引数は、i1
型のllvm::Value*
構造体
// 構造体の宣言 // 中身は後で定義する // こうすると構造体のメンバーに自分自身へのポインタを含めることができる // これはC言語の`struct StructName;`に相当する llvm::StructType* structType = llvm::StructType::create(context, "StructName"); // 構造体の中身を定義 // 例:構造体 {i32, %StructName*} の場合 llvm::Type* types[] = { // std::vector<llvm::Type*>型でも良い llvm::Type::getInt32Ty(context), llvm::PointerType::get(structType, 0) }; structType->setBody(types, false); // 構造体変数の定義 // 普通の変数と同様にallocaするだけ llvm::Value* structValue = builder.CreateAlloca(structType); // ゼロ初期化 // `zeroinitializer`を`store`すると全メンバを0値で初期化できる llvm::Value* zero = llvm::Constant::getNullValue(structType); builder.CreateStore(zero, structValue); // 構造体メンバのポインタを取得 // getelementptr で取得する // 第二引数で、何番目のメンバを取得するかを指定する(先頭は0) llvm::Value* memberPtrFirst = builder.CreateStructGEP(structValue, 0); llvm::Value* memberPtrSecond = builder.CreateStructGEP(structValue, 1); // 定数`%StructName{ i32 123,% StructName * null }`を作成 llvm::Constant* values[] = { // std::vector<llvm::Constant*>型でも良い llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 123, true), llvm::Constant::getNullValue(llvm::PointerType::get(structType, 0)) }; llvm::Constant* structConstant= llvm::ConstantStruct::get(structType, values);
あとの読み書き(load
、store
)は普通の変数と同様。
context
はllvm::LLVMContext
型の変数。説明はこちら
自分自身の型を参照しないなら、llvm::StructType::create()
の時点で中身も同時に定義してよい。
llvm::StructType::get()
というAPIもあるが、こちらはなぜか毎回おなじポインタを返してくるので、複数の構造体を定義しようとしたら実は1つしか作れていないという事態になった。
structType->setBody(types, false);
の第二引数はpackするかどうかのフラグ。
構造体のpackについてはLLVM言語のドキュメント(英語)を参照。
大まかにいうと、packするとアライメントのためのパディングがなくなる。よくわからない場合は常にfalse
にしておけばよい。
llvm::Constant::getNullValue()
は「ゼロ値」を生成する。数値型を指定した場合は0
、ポインタ型を指定した場合はnull
、構造体や配列を指定した場合はzeroinitializerになる。
※常にzeroinitializer
を生成するllvm::ConstantAggregateZero::get()
もあるが、これはなぜか複数回実行すると後でllvm::legacy::PassManager::run()
を実行したときにLLVM ERROR: out of memory
というエラーになる。ConstantAggregateZeroの方は使うべきではない模様。
llvm::ConstantStruct::get()
に構造体の型と値のリストを渡すと、構造体型の定数(C言語におけるリテラル)を作成できる。
配列
配列の型は以下のように作成する。
// 第一引数に配列の要素の型、第二引数に配列サイズを指定
llvm::ArrayType *arrayType = llvm::ArrayType::get(elementType, arraySize);
配列変数の作成は普通の変数と同様にCreateAlloca
等で行う。
// 定数`[3 x i32] [i32 1, i32 2, i32 3]`を作成 llvm::Constant* values[] = { llvm::ConstantInt::get(elementType, 1), llvm::ConstantInt::get(elementType, 2), llvm::ConstantInt::get(elementType, 3), }; llvm::Constant* arrayLiteral = llvm::ConstantArray::get(arrayType, values);
配列の要素を取得する場合は以下。
llvm::Constant* indexes[] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 0), llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), index), }; // 第一引数に配列の型、第二引数に配列へのポインタ、第三引数にgetelementptrのインデックスを指定 // getelementptrのインデックスは、配列の場合は、1個目が常に0、2個目が配列のインデックス elementPtr = builder_.CreateGEP(arrayType, arrayValue, indexes);
仮に配列の型を[10 x i32]
として、その3番目の要素を取得したい場合、上記はLLVM言語のgetelementptr [10 x i32], [10 x i32]* %arrayValue, i32 0, i32 2
に相当する。