単子葉類プログラマーのメモ

プログラミング関連の自分用メモだけど他の人の役に立つかもしれないので公開しておく感じのブログ

LLVM C++ API 学習メモ(5) - 分岐、構造体、配列

分岐、構造体、配列に関するLLVM C++ APIについて。

LLVMのバージョンは10.0.0

目次

分岐

LLVMにはif else文そのものや、do、while、forもない。

brで再現する必要がある。brは無条件でもジャンプできるし、条件にあわせて行先を指定することもできる。

LLVM言語のbrについての説明はこちら

LLVMC++ APIは以下。

// 無条件分岐
builder.CreateBr(destBlock);

// 条件付き分岐
builder.CreateCondBr(i1Value, thenBlock, elseBlock);

builderllvm::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);

あとの読み書き(loadstore)は普通の変数と同様。

contextllvm::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等で行う。

定数(C言語におけるリテラル)は以下のように作成する。

// 定数`[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に相当する。