TechCraft – エンジニアのためのスキルアップメモ

エンジニアのスキルアップを少しでも加速する技術ブログ

RustからLLVM

RustからLLVMを利用する方法 – 中間表現と最適化の世界を体験しよう

1. はじめに

Rustはコンパイルの裏側でLLVM(Low-Level Virtual Machine)という強力なバックエンドコンパイラ基盤を使用しています。
このLLVMは、コンパイル最適化や中間表現(IR)生成の要であり、Rustの安全・高速な性能の裏支えでもあります。

本記事では、RustからLLVMの機能を直接使ってIRを出力したり、最適化パスを確認する方法を紹介します。
コンパイラの内部やパフォーマンスに興味のある方におすすめの内容です。


2. LLVMとは?

LLVMは、低レベルの抽象化を行う中間表現(IR)と、それを操作・最適化するためのパス(Pass)やツールチェインから成る汎用コンパイラ基盤です。

Rustは rustc のバックエンドにLLVMを使っており、最終的にはLLVM IR → 機械語という流れでコードを生成しています。


3. RustからLLVM IRを出力する

3.1 環境準備

RustとLLVMは既にrustupを使えば簡単に導入されています。

rustup update
rustc --version

3.2 簡単なRustコードを用意

// hello.rs
fn main() {
    println!("Hello, LLVM!");
}

3.3 LLVM IRを出力する

rustc --emit=llvm-ir hello.rs

このコマンドにより、hello.ll というLLVM IRファイルが生成されます。

ls
hello.rs  hello.ll

3.4 出力されたIRを確認

cat hello.ll

出力の一部(例):

; ModuleID = 'hello'
source_filename = "hello.rs"

define internal void @_ZN4core3fmt5write17h...() {
  ...
}

この中間表現はLLVMの文法で記述されたテキスト形式のIRであり、LLVMoptllcといったツールで更に操作可能です。


4. 最適化レベルの違いを比較する

RustはLLVMの最適化フラグを-C opt-level=で制御できます。

# 最適化なし
rustc --emit=llvm-ir -C opt-level=0 hello.rs -o hello_opt0.ll

# 最適化強め
rustc --emit=llvm-ir -C opt-level=3 hello.rs -o hello_opt3.ll

差分を見るには以下のように:

diff hello_opt0.ll hello_opt3.ll

最適化の有無によって関数のインライン展開、不要コード削除、ループ最適化などの違いが確認できます。


5. llvm-tools-previewと外部ツールの活用

5.1 llvm-tools-preview のインストール

rustup component add llvm-tools-preview

これにより、以下のようなツールがRust toolchainから使用可能になります:

  • llvm-dis: バイナリIR(.bc) → テキストIR(.ll)へ
  • llvm-nm: シンボルの列挙
  • opt: 最適化パスの適用
  • llc: 機械語への変換

5.2 .bc形式で出力してツールで確認

rustc --emit=llvm-bc hello.rs

出力された hello.bc に対して:

llvm-dis hello.bc -o hello_manual.ll

または最適化を試す:

opt -O3 hello.bc -o hello_opt.bc
llvm-dis hello_opt.bc -o hello_opt.ll

6. RustコードからLLVM IRをカスタム生成するには?

もし「RustでLLVM IRを直接制御したい」「独自のIRを生成したい」場合は以下のクレートが有効です。

おすすめのライブラリ:

inkwellのサンプル:

use inkwell::context::Context;

fn main() {
    let context = Context::create();
    let module = context.create_module("main");
    let builder = context.create_builder();

    let i32_type = context.i32_type();
    let fn_type = i32_type.fn_type(&[], false);
    let function = module.add_function("main", fn_type, None);
    let basic_block = context.append_basic_block(function, "entry");
    builder.position_at_end(basic_block);
    builder.build_return(Some(&i32_type.const_int(0, false)));

    module.print_to_stderr();
}

これを使えば、RustからLLVMのIRを自由に構築・出力できます。


7. まとめ

操作 コマンド
LLVM IR 出力 rustc --emit=llvm-ir hello.rs
LLVM BC 出力 rustc --emit=llvm-bc hello.rs
最適化確認 -C opt-level=3
LLVMツール操作 opt, llvm-dis, llc, llvm-nm
カスタムIR生成 inkwell クレートで安全に構築

RustからLLVMを活用することで、コンパイルの仕組みや最適化の裏側に触れられるだけでなく、
独自言語・JITコンパイラ開発といった高度な応用にもつながります。


参考リンク

参考書籍