---
title: LLVM IR 実践
url: https://doc.liz6.com/ja/compilers/04-intermediate-representation/02-llvm-ir-in-practice
locale: ja
area: compilers
tags:
- compilers
- intermediate-representation
date: 2026-06-30
modified: 2026-07-19
description: LLVM IR は SSA の教科書的な例に留まらない——Clang、Rustc、Swiftc など数十のコンパイラフロントエンドが共有する「ターゲット言語」であり、完全なSSA、強型付け、3アドレスコードである。IRの構文とメモリモデルを理解して初めて、ASTからLLVMへのコード生成を自前で実装できる。
---

# LLVM IR 実践

> LLVM IR は SSA の教科書的な例に留まらない——Clang、Rustc、Swiftc など数十のコンパイラフロントエンドが共有する「ターゲット言語」であり、完全なSSA、強型付け、3アドレスコードである。IRの構文とメモリモデルを理解して初めて、ASTからLLVMへのコード生成を自前で実装できる。

## 概要

[SSA形式](/compilers/04-intermediate-representation/01-ssa-form.md)では、SSAの数学的基盤——支配木/φ関数/Cytronアルゴリズム——について解説した。ここではLLVM IRに焦点を当て、LLVMの中間表現であるLLVM IRは、完全なSSA、強型付け、3アドレスコード形式であり、Clang、Rustc、Swiftc など数十のコンパイラフロントエンドの共通ターゲットであり、LLVM最適化パイプラインの入力でもある。本稿では「LLVMにはどのようなpassがあるか」ではなく、LLVM IRを一種の言語として捉え、その構文、セマンティクス、型、メモリモデルについて解説する——これらを理解して初めて、「ASTからLLVM IRへの降下」コード生成を自前で実装できる。

## LLVM IRの3つの形式

同じLLVMモジュールには3つの表現があり、これらは互いに同等である：

```
.ll (人間可読テキスト):         ドキュメントやデバッグで定義・参照される
  %result = add i32 %a, %b
  ret i32 %result

.bc (ビットコードバイナリ):      リンクやLTO用のコンパクトな形式
  (バイナリシーケンス)

メモリIR (C++ API):             コンパイラフロントエンドがメモリ上で構築
  auto *add = builder.CreateAdd(a, b, "result");
```

3つの形式は `llvm-as` / `llvm-dis` / `opt` / `llc` によって相互に変換される。`.ll` テキスト形式を理解することは、LLVM IRの読解やデバッグを行うための前提条件である。

## コア構文: 仮想レジスタ + 型 + 命令

### 仮想レジスタと代入

LLVM IRは**完全なSSA**である——各仮想レジスタ(`%1`, `%2`, ...または`%name`)はちょうど1回だけ代入される：

```llvm
%x = add i32 1, 2          ; %x が定義される
%y = mul i32 %x, 3          ; %x が使用され、%y が定義される
; %x に再度代入することはできない — SSA違反となるため、φ関数または別のバージョンを使用する必要がある
```

### 型システム

LLVM IRの型はCよりもはるかに豊富で明確であり、すべての値に型が存在する：

```llvm
i1          ; 1ビット整数 (ブール値)
i32         ; 32ビット整数
i64         ; 64ビット整数
float       ; 32ビットIEEE浮動小数点数
double      ; 64ビットIEEE浮動小数点数
ptr         ; 不透明ポインタ (opaque pointer, LLVM 15以降)
            ; 旧版: i32* (i32へのポインタ)

[10 x i32]  ; i32が10個の配列
{ i32, float, i8 }   ; 構造体 (匿名)
<4 x float>           ; floatが4個のベクトル (SIMD)
```

**不透明ポインタ**(`ptr`)は**重要な簡素化**である。LLVM 15以前はポインタが `i32*`、`float**` などであった——型とポインタのビット幅は直交していた。不透明ポインタは「ポインタがどの型を指すか」というエンコーディングを排除し、IRをより簡潔にした（`bitcast` の回数を削減したが、フロントエンドは `gep` 用に型情報を自行で維持する必要がある）。

### 制御流命令

LLVM IRの制御流は基本ブロックと終端命令に基づく：

```llvm
define i32 @max(i32 %a, i32 %b) {
entry:
  %cond = icmp sgt i32 %a, %b       ; 符号付き比較: %a > %b → i1
  br i1 %cond, label %then, label %else

then:
  ret i32 %a                         ; 終端: 戻り値

else:
  ret i32 %b
}
```

- `br i1 %cond, label %true, label %false`: 条件分岐
- `br label %dest`: 無条件ジャンプ
- `ret <ty> <value>` / `ret void`: 戻り値
- `switch i32 %val, label %default [...]`: 多路ジャンプ
- `indirectbr`: 計算によるジャンプ (仮想関数テーブルディスパッチなど)

### Phi命令: 制御流の合流点での値選択

```llvm
define i32 @abs(i32 %x) {
entry:
  %ge = icmp sge i32 %x, 0
  br i1 %ge, label %pos, label %neg

pos:
  br label %merge

neg:
  %negx = sub i32 0, %x
  br label %merge

merge:
  %result = phi i32 [ %x, %pos ], [ %negx, %neg ]
  ; result = posブロックから来たら %x、negブロックから来たら %negx
  ret i32 %result
}
```

phiの引数 `[value, predecessor_block]` は厳密にバインドされる——最初の value は最初の predecessor から進入した場合に対応する。phi命令は基本ブロックの先頭に配置され、複数のphiは宣言順に並ぶ——これらは実際の計算を表すものではなく、制御流の合流点での値選択であり、すべてそのブロックに入る直前（つまりpredecessorブロック内）のバージョンの参照である。

## メモリモデル: alloca / load / store / gep

LLVM IRは**仮想レジスタ**（SSA値、レジスタ上）と**メモリ**（スタック/ヒープ上、ポインタ経由でアクセス）を区別する：

```llvm
%ptr = alloca i32               ; スタック上に i32 を割り当て (ptr を返す)
store i32 42, ptr %ptr          ; ptr が指すメモリに 42 を書き込む
%val = load i32, ptr %ptr       ; ptr から読み戻し → %val (i32)

%x = add i32 %val, 1            ; SSA値に対して直接演算
store i32 %x, ptr %ptr
```

`alloca` の結果は `ptr` であり、その後の `load`/`store` は明示的にメモリを読み書きする。**LLVMの mem2reg pass は「昇格可能な alloca + load/store ペア」をSSA仮想レジスタ+phiに変換する**——これは最適化パイプラインに入る前の最初の関門であり、昇格不可能な（アドレスが取得される、関数を跨いで渡されるなど）ものはメモリに残る。

### GEP(GetElementPtr): 構造体/配列のアドレス計算

`getelementptr` はLLVM IRの中で最も混乱を招きやすい命令であり、そのセマンティクスは**純粋なアドレス計算であり、メモリにアクセスしない**ことである：

```llvm
%struct = type { i32, float, i8 }           ; 構造体定義

%p = alloca %struct
%field1_addr = getelementptr %struct, ptr %p, i32 0, i32 1
;                                            ^^  ^^   ^^
;                                         type  ptr  インデックス:
;                                                  [0]=間接参照なし, [1]=1番目のフィールド (float)
store float 3.14, ptr %field1_addr
```

GEPの計算は型レイアウトに基づいて完全に行われる——`[i32 0, i32 1]` とは「`%p` から出発し、`%struct` という型に対して、0番目の要素（ポインタを変更せず）、1番目のフィールドを取得する」ことを意味する。GEPはベースアドレスからのオフセットポインタを返す（構造体フィールドの型レイアウトによって決定される）だけで、メモリにはアクセスしない——`i32` は4バイト、`float` は4バイト境界にアライン、`i8` は1バイトである。

現代のLLVMの不透明ポインタでは、GEPの最初のインデックスは通常常に `i32 0` である（歴史的なポインタ間接参照はno-opになる）。

## 関数とモジュール構造

```llvm
; 外部宣言
declare i32 @printf(ptr, ...)

; 関数定義
define i32 @main(i32 %argc, ptr %argv) {
  ...
  %fmt = ... ; "Hello %s\n" を指すポインタ
  call i32 (ptr, ...) @printf(ptr %fmt, ptr %str)
  ret i32 0
}
```

- `@name`: グローバルシンボル（関数、グローバル変数）
- `%name`: ローカル仮想レジスタ
- `call`: 呼び出し——直接呼び出し、間接呼び出し(`call i32 %fp(...)`)、末尾呼び出し(`musttail call`)が可能

## ASTからLLVM IRへ: 1つの降下パス

`a + b * c` を例に、ASTからLLVM IRへの降下プロセスを示す：

```
AST:  Binary(+, Variable("a"), Binary(*, Variable("b"), Variable("c")))

Codegen (再帰):
  codegen(Binary(+, lhs, rhs)):
    left_val  = codegen(lhs)         → %a = load i32, ptr %a_addr
    right_val = codegen(rhs)        → 再帰:
                                        b_val = codegen(Var("b")) → %b = load...
                                        c_val = codegen(Var("c")) → %c = load...
                                        → %tmp = mul i32 %b, %c
    → %add = add i32 %a, %tmp
    return %add

出力 LLVM IR:
  %a = load i32, ptr %a_addr
  %b = load i32, ptr %b_addr
  %c = load i32, ptr %c_addr
  %tmp = mul i32 %b, %c
  %add = add i32 %a, %tmp
```

実際のcodegenでは以下も処理する必要がある：
- **ショートサーキット評価** (`a && b`): `if a { if b { true } else { false } } else { false }` の制御流+phiに変換。
- **変数のアドレス** (`&x`): xが以前にallocaされていればそのptrを直接使用；xがすでにmem2regで仮想レジスタに昇格されていれば、再度allocaしてstoreする必要がある。
- **集約型**: 構造体と配列の代入はmemcpyまたはフィールドごとのload/storeに変換される。

## 参考

- **LLVM Language Reference Manual**: https://llvm.org/docs/LangRef.html — IRの完全な構文とセマンティクス。公式を基準とする
- **LLVM Kaleidoscope Tutorial**: https://llvm.org/docs/tutorial/ — DSLからLLVM IRへのゼロからの実装
- **"Learn LLVM 17"**(Kai Nacke) — LLVMコアのエンジニアリングガイド。pass manager、バックエンド、JITを含む

*Keywords: LLVM IR, SSA, virtual register, i1, i32, i64, ptr, opaque pointer, struct, array, vector, phi, br, switch, alloca, load, store, gep, getelementptr, bitcast, function, module, intrinsic, lowering, mem2reg, Kaleidoscope*
