inkwell

LLVM C API を安全にラップした Rust の LLVM バインディングクレート。トイ言語コンパイラ toy-llvm-compiler (ipulang) で LLVM IR 生成に使用 (LLVM 12 系)。生成した .llclang で実行ファイルへリンクする。

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_comparecond != 0) で結線。各ブロック生成前に position_at_end を呼ぶ必要がある。
  • 組み込み関数 (putchar/getchar) は IR 内で 宣言だけ しておけばリンク時に解決され呼べる。
  • 最後に module.print_to_string() で IR 文字列を得る。

コード生成の状態管理

コンパイラ側は Env 構造体に module / ctx / 関数ごとの変数マップ / 一時変数カウンタ / 関数テーブル / 現在の builder・関数 を保持し、各 AST ノードに gen_code(&mut env) を実装して Program から再帰的に呼ぶ (後に trait CodeGen へ整理)。

関連

原典メモ(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);

大体同じことしてた