inkwell
LLVM C API を安全にラップした Rust の LLVM バインディングクレート。トイ言語コンパイラ toy-llvm-compiler (ipulang) で LLVM IR 生成に使用 (LLVM 12 系)。生成した .ll を clang で実行ファイルへリンクする。
IR の階層と生成
- LLVM IR は
Module > Function > Block > Instructionの階層構造。Builderのメソッド呼び出しで手続き的に命令を組む。 - 変数:
build_allocaでスタック確保 →build_store/build_load。二項加算はbuild_int_add。 - 関数:
i32_type.fn_type(args, is_vararg)で型宣言し、entryブロックを作って引数を ptr に load。 - 制御フロー:
append_basic_blockでブロックを作り、build_unconditional_branch/build_conditional_branch(build_int_compareでcond != 0) で結線。各ブロック生成前にposition_at_endを呼ぶ必要がある。 - 組み込み関数 (
putchar/getchar) は IR 内で 宣言だけ しておけばリンク時に解決され呼べる。 - 最後に
module.print_to_string()で IR 文字列を得る。
コード生成の状態管理
コンパイラ側は Env 構造体に module / ctx / 関数ごとの変数マップ / 一時変数カウンタ / 関数テーブル / 現在の builder・関数 を保持し、各 AST ノードに gen_code(&mut env) を実装して Program から再帰的に呼ぶ (後に trait CodeGen へ整理)。
関連
- toy-llvm-compiler、llvm-mlir、rust-lang。
- 命令へ落とすバックエンド側は compiler-optimization、対象 ISA は risc-v-isa。
原典メモ(notes/Inkwell より統合)
Inkwell
basic
let context = Context::create();
// module
let module = context.create_module("main");
// builder
let builder = context.create_builder();
// i32
let i32_type = context.i32_type();
// main: () -> i32
let main_fn_type = i32_type.fn_type(&[i32_type.into()], false);
let main_fn = module.add_function("main", main_fn_type, None);
// block
let basic_block = context.append_basic_block(main_fn, "entry");
builder.position_at_end(basic_block);
// a = 3, b = 5
let a = builder.build_alloca(context.i32_type(), "a");
let b = builder.build_alloca(context.i32_type(), "b");
let three = i32_type.const_int(3, false);
let five = i32_type.const_int(5, false);
builder.build_store(a, three);
builder.build_store(b, five);
// load a, b
let bload = builder.build_load(b, "bload");
let aload = builder.build_load(a, "aload");
// add a, b
let c = builder.build_int_add(aload.into_int_value(), bload.into_int_value(), "c");
builder.build_return(Some(&c));
function declare
// decrare builtin function
let i32_type = ctx.i32_type();
let putchar_type = i32_type.fn_type(&[i32_type.into()], false);
functions.insert("putchar".to_owned());
module.add_function("putchar", putchar_type, None);