てけもぐ Tech 忘備録

LLVM-C API でやる hello world

対象読者

LLVM-C API 初学者

解決すること

LLVM-C API の簡単な実行例

内容

LLVM-C API を使った hello world をメモしておく。

C のコード。main.c として保存して下の makefile でコンパイル。

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

LLVMModuleRef module = NULL;
LLVMBuilderRef builder = NULL;

LLVMValueRef gen_main() {

    // main() definition
    LLVMTypeRef type_main_params[] = {};
    LLVMTypeRef type_main =
        LLVMFunctionType(LLVMInt32Type(), type_main_params, 0, 0);
    LLVMValueRef main = LLVMAddFunction(module, "main", type_main);

    // add block in main
    LLVMBasicBlockRef entry = LLVMAppendBasicBlock(main, "entry");
    LLVMPositionBuilderAtEnd(builder, entry);

    // declaration of int printf(char *fmt, ...);
    LLVMTypeRef type_fmt =
        LLVMPointerType(LLVMPointerType(LLVMInt8Type(), 0), 0);
    LLVMTypeRef type_printf_params[] = {type_fmt};
    LLVMTypeRef type_printf =
        LLVMFunctionType(LLVMInt32Type(), type_printf_params, 1, 1);
    LLVMValueRef func = LLVMAddFunction(module, "printf", type_printf);

    // define "char *fmt = "hello world!\n";
    LLVMValueRef fmt =
        LLVMBuildGlobalStringPtr(builder, "hello world!\n", "hello_world");

    // call printf()
    LLVMValueRef args[] = {fmt};
    LLVMBuildCall2(builder, type_printf, func, args, 1, "printf");

    // return from main()
    return LLVMBuildRet(builder, LLVMConstInt(LLVMInt32Type(), 0, 0));
}

int main(int ac, char *av[]) {

    // initilize llvm
    module = LLVMModuleCreateWithName("my_module");
    builder = LLVMCreateBuilder();

    // generate main ir
    gen_main();

    // dump llvm ir to stdout
    printf("%s", LLVMPrintModuleToString(module));

    // dispose llvm builder and module
    LLVMDisposeBuilder(builder);
    LLVMDisposeModule(module);

    return 0;
}

makefile は以下の様に。

all: a.out

a.out: a.s
	clang -no-pie -o $@ $^

a.s: a.ll
	llc -o $@ $^

a.ll: main.out
	./main.out > $@

main.out: main.c
	gcc -g -o ${@:%.out=%.o} -c $^ `llvm-config --cflags` ;\
	g++ -g ${@:%.out=%.o} -o $@ `llvm-config --cxxflags --ldflags --libs all`

clean:
	rm -f *.s *.out *.o *.ll

.PHONY: clean
.PRECIOUS: %.ll %.s %.out

途中で出力される IR(a.ll)は、以下のように。

; ModuleID = 'my_module'
source_filename = "my_module"

@hello_world = private unnamed_addr constant [14 x i8] c"hello world!\0A\00", align 1

define i32 @main() {
entry:
  %printf = call i32 (ptr, ...) @printf(ptr @hello_world)
  ret i32 0
}

declare i32 @printf(ptr, ...)

make をそのまま実行して、./a.out とすると、hello world! が出力される。

hello world のわりには長め。型の定義に多少手間がかかる。