LLVM C APIの使い方メモ

とりあえず動くところまでできたのでメモ

How to get started with the LLVM C APIを参考に書いた.

関数の挙動とか雰囲気で書いてるので間違ってるかもしれない

ソースコードと実行

ソースコード

main.c

#include <stdio.h>
#include <llvm-c/Core.h>

int main() {
   /* Moduleの作成 */
   LLVMModuleRef module = LLVMModuleCreateWithName("test");

   /* Functionの作成 */
   // パラメータの型の配列
   LLVMTypeRef paramTypes[] = { LLVMInt32Type(), LLVMInt32Type() };
   // 関数の型
   LLVMTypeRef funType = LLVMFunctionType(LLVMInt32Type(), paramTypes, 2, 0);
   // Moduleに関数を追加
   LLVMValueRef fun = LLVMAddFunction(module, "sum", funType);

   /* BasicBlockの作成 */
   // 指定した関数にBasicBlockを追加
   LLVMBasicBlockRef basicBlock = LLVMAppendBasicBlock(fun, "");

   /* InstructionをBasicBlockに書き込む */
   // Builderの作成
   LLVMBuilderRef builder = LLVMCreateBuilder();
   // Builderの書き込み位置を設定(?)
   LLVMPositionBuilderAtEnd(builder, basicBlock);
   // add命令を書き込み
   LLVMValueRef tmp = LLVMBuildAdd(builder, LLVMGetParam(fun, 0), LLVMGetParam(fun, 1), "");
   // ret命令を書き込み
   LLVMBuildRet(builder, tmp);
   // Builderを破棄
   LLVMDisposeBuilder(builder);

   /* ファイルにLLVMのファイル(.ll形式)を書き込み */
   char *errorMessage = NULL;
   // 成功したときは0, 失敗したらそれ以外
   if( LLVMPrintModuleToFile(module, "test.ll", &errorMessage) ) {
      fprintf(stderr, "%s\n", errorMessage);
      // エラーメッセージを破棄
      LLVMDisposeMessage(errorMessage);
   }

   // Moduleを破棄
   LLVMDisposeModule(module);
}

コンパイル

ライブラリの指定は-lLLVM-5.0でできる.(少なくともArch Linuxでは)

実行結果

test.ll

; ModuleID = 'test'
source_filename = "test"

define i32 @sum(i32, i32) {
  %3 = add i32 %0, %1
  ret i32 %3
}

できてそう

LLVM言語の構造

Moduleの中にFunctionがいくつかある.

Functionの中にBasicBlockがいくつかある.

BasicBlockの中に命令列がある.

BasicBlockは間にジャンプ元・ジャンプ先を持たない命令列.(たぶん)

(GlobalVariableは省略.)

このへんは「きつねさんでもわかるLLVM」に書いてある.

関数の使い方など

かなり適当

Core.h File Referenceに書いてあるけどよくわからないのでだいたいエスパー.

型名にでてくるRefは実質ポインタだと思ってる.

Module


LLVMModuleRef LLVMModuleCreateWithName (const char *ModuleID)

ModuleIDを名前とするModuleを作成する.


LLVMBool LLVMPrintModuleToFile (LLVMModuleRef M, const char *Filename, char **ErrorMessage)

Filenameで指定したファイルにMで指定したModuleのLLVM言語を出力する.

成功したときは0,失敗したときはそれ以外を返す. 失敗したときにはerrorMessageで指定したポインタにエラーメッセージの先頭アドレスが書き込まれる.

エラー時のエラーメッセージはLLVMDisposeMessageで破棄しないとメモリリークを起こす.


void LLVMDisposeModule (LLVMModuleRef M)

Moduleを破棄する. fsanitize=addressしてもメモリリークしてないのでおそらく内部のFunctionとかも破棄される.


Function


LLVMTypeRef LLVMFunctionType (LLVMTypeRef ReturnType, LLVMTypeRef *ParamTypes, unsigned ParamCount, LLVMBool IsVarArg)

ReturnTypeを戻り値の型,ParamTypesをパラメータの型,ParamCountを変数の数として関数の型を作成する. IsVarArgはよくわからない.


LLVMValueRef LLVMAddFunction (LLVMModuleRef M, const char *Name, LLVMTypeRef FunctionTy)

Mで関数を追加するModule,Nameで関数名,FunctionTyで関数の型を指定して関数を追加する.


BasicBlock


LLVMBasicBlockRef LLVMAppendBasicBlock (LLVMValueRef Fn, const char *Name)

Fnで指定した関数の後ろ(?)にBasicBlockを追加する.

Nameでラベル名が指定できる.よくわからないがとりあえず空文字列を指定しておけばunnamed valueになる.


Builder


LLVMBuilderRef LLVMCreateBuilder (void)

Builderを作成する.


void LLVMDisposeBuilder (LLVMBuilderRef Builder)

Builderを破棄する.


void LLVMPositionBuilderAtEnd (LLVMBuilderRef Builder, LLVMBasicBlockRef Block)

Builderの書き込み位置を指定(?)する.


命令生成


LLVMValueRef LLVMGetParam (LLVMValueRef Fn, unsigned Index)

関数FnIndex番目(0-indexed)のパラメータを取得する.


LLVMValueRef LLVMBuildAdd (LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS, const char *Name)

LLVMのadd命令を追加する. LLVMAppendBasicBlockと同様Nameを空文字列にしておけば戻り値をunnamed valueにしてくれる.


LLVMValueRef LLVMBuildRet (LLVMBuilderRef, LLVMValueRef V)

LLVMのret命令を追加する.


参考