Files
hakorune/docs/private/roadmap/phases/phase-21-optimization/llvm-optimization-strategy.md

337 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LLVM最適化戦略Gemini提案統合版
**出典**: Gemini AI提案2025-10-02
**統合者**: Claude (Sonnet 4.5)
---
## 🎯 **基本方針**
nyashの14個の命令 → LLVMの何百もの低レベル命令
この「言葉のレベルの違い」をうまく利用するのが最適化の鍵!
---
## 📚 **最適化手法4つの柱**
### **1. LLVM Intrinsics魔法の関数を使う** 🪄
LLVMには `@llvm.` で始まる特別な関数Intrinsicsがある。
これらは**LLVMのオプティマイザが特別な意味を知っている**魔法の関数!
#### **主要Intrinsics**
| Intrinsic | 用途 | 効果 |
|-----------|------|------|
| `@llvm.memcpy` | メモリ一括コピー | CPUの最速命令に変換 |
| `@llvm.memmove` | オーバーラップ対応コピー | 安全な一括コピー |
| `@llvm.memset` | メモリ初期化 | 一括ゼロクリア |
| `@llvm.sqrt.f64` | 平方根計算 | CPU命令1個に変換 |
| `@llvm.abs.i64` | 絶対値計算 | 分岐なし高速化 |
| `@llvm.bswap.i64` | バイトスワップ | エンディアン変換 |
#### **実装例: メモリコピー最適化**
```python
# ❌ Before: 自前ループ(遅い)
def copy_array_slow(dst, src, length):
for i in range(length):
dst_ptr = builder.gep(dst, [ir.Constant(i64, i)])
src_ptr = builder.gep(src, [ir.Constant(i64, i)])
val = builder.load(src_ptr)
builder.store(val, dst_ptr)
# ✅ After: LLVM Intrinsic速い
def copy_array_fast(dst, src, length):
memcpy_func = declare_llvm_memcpy(module)
builder.call(memcpy_func, [
dst, # destination
src, # source
length, # byte count
ir.Constant(i1, 0) # is_volatile
])
```
**効果**:
- ループオーバーヘッド削減
- SIMD命令への自動変換
- CPUのDMA機能活用
---
### **2. Box構造をLLVMに教える** 📦
nyashでは「Everything is Box」だが、LLVMには具体的な構造体として伝える必要がある。
#### **Box構造の明示**
```python
# StringBox の構造定義
StringBox = ir.LiteralStructType([
ir.IntType(32), # 0: length
ir.IntType(8).as_pointer() # 1: data*
])
# IntegerBox の構造定義
IntegerBox = ir.LiteralStructType([
ir.IntType(64) # 0: value
])
```
#### **extractvalue命令への置き換え**
```python
# ❌ Before: 関数呼び出し(遅い)
length_func = module.get_function("StringBox.length")
length = builder.call(length_func, [string_box])
# ✅ After: 構造体アクセス(速い)
length = builder.extract_value(string_box, 0, name="length")
# → たった1命令関数呼び出しオーバーヘッドなし
```
**効果**:
- 関数呼び出しコスト削減10-100倍高速化
- インライン展開促進
- レジスタ割り当て最適化
---
### **3. TBAAType-Based Alias Analysisヒント** 🎯
「このポインタとあのポインタは別物だよ」とLLVMに教えてあげる。
#### **TBAAメタデータの設定**
```python
class TBAABuilder:
"""TBAA型ベースエイリアス解析メタデータ生成"""
def __init__(self, module):
self.module = module
self.root = self._create_tbaa_root()
self.box_types = {}
def _create_tbaa_root(self):
"""TBAA階層のルート"""
return self.module.add_metadata([
ir.MetaDataString(self.module, "nyash-tbaa")
])
def create_box_type(self, box_name):
"""Box型ごとのTBAAード"""
if box_name not in self.box_types:
self.box_types[box_name] = self.module.add_metadata([
ir.MetaDataString(self.module, box_name),
self.root,
ir.IntType(64)(0) # offset
])
return self.box_types[box_name]
def annotate_load(self, load_inst, box_name):
"""load命令にTBAAメタデータ付与"""
tbaa_node = self.create_box_type(box_name)
load_inst.set_metadata("tbaa", tbaa_node)
# 使用例
tbaa = TBAABuilder(module)
# StringBoxへのアクセス
str_load = builder.load(string_ptr)
tbaa.annotate_load(str_load, "StringBox")
# IntegerBoxへのアクセス
int_load = builder.load(integer_ptr)
tbaa.annotate_load(int_load, "IntegerBox")
```
**効果**:
```python
# TBAA情報があると、LLVMはこう最適化できる:
# ① 整数を読む
int_val = load(integer_box)
# ② 文字列を書き込むIntegerBoxとは無関係
store(string_val, string_box)
# ③ もう一度同じ整数を読む
# → LLVMは「②の書き込みは①の整数に影響しない」と判断
# → ③の再読み込みを省略! int_val を再利用
int_val_reused = int_val # load削減
```
---
### **4. 関数属性Function Attributes** 🏷️
関数の「性格」をLLVMに伝える。
#### **主要属性**
| 属性 | 意味 | 対象関数例 |
|-----|------|-----------|
| `readnone` | 副作用なし・メモリ読まない | Math.add, Math.sqrt |
| `readonly` | 読み込みのみ・書き込みなし | Array.length, String.length |
| `nounwind` | 例外を投げない | すべてのnyash関数 |
| `alwaysinline` | 常にインライン展開 | 小さいヘルパー関数 |
| `noinline` | インライン展開禁止 | デバッグ用関数 |
| `cold` | めったに実行されない | エラーハンドラ |
#### **実装例**
```python
def annotate_pure_function(func):
"""純粋関数の最適化"""
# 副作用なし → LLVMが同じ引数の呼び出しを1回に最適化
func.attributes.add("readnone")
func.attributes.add("nounwind")
# 小さい関数は積極的にインライン化
if count_instructions(func) < 10:
func.attributes.add("alwaysinline")
# 使用例
math_add_func = module.get_function("Math.add")
annotate_pure_function(math_add_func)
# ✅ 最適化結果:
# result1 = Math.add(1, 2) # → 実行される
# result2 = Math.add(1, 2) # → LLVMが省略result1を再利用
```
---
## 🚨 **注意点(絶対守るべきルール)**
### **1. 未定義動作UBを絶対に避ける** ⚠️
LLVMは未定義動作があると**とんでもないコードを生成する**ことがある!
#### **主要UB一覧**
| UB | 例 | 対策 |
|---|---|------|
| nullポインタ参照 | `*null_ptr` | null check挿入 |
| 符号付きオーバーフロー | `INT_MAX + 1` | チェック付き演算 |
| 未初期化変数使用 | `int x; return x;` | 明示的初期化 |
| 配列境界外アクセス | `arr[length]` | 境界チェック |
#### **実装例: null check**
```python
def safe_load(ptr, name="load"):
"""null check付きload"""
# null check
is_null = builder.icmp_unsigned('==', ptr, ir.Constant(ptr.type, None))
with builder.if_then(is_null):
# nullならエラー
builder.call(panic_func, [
ir.Constant.literal_struct([
ir.Constant(i8p, "Null pointer dereference")
])
])
# 安全にload
return builder.load(ptr, name=name)
```
---
### **2. データレイアウト・呼び出し規約の統一** 📐
プラットフォーム別の標準ルールに従う。
```python
# ターゲット別のデータレイアウト
TARGET_LAYOUTS = {
"x86_64-unknown-linux-gnu":
"e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"wasm32-unknown-unknown":
"e-m:e-p:32:32-i64:64-n32:64-S128",
"aarch64-unknown-linux-gnu":
"e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
}
# モジュールに設定
module.triple = target_triple
module.data_layout = TARGET_LAYOUTS[target_triple]
```
---
### **3. PHI命令は慎重に** 🔀
全ての前任ブロックからの値を漏れなく指定!
```python
# ✅ 正しいPHI
phi = builder.phi(i64, name="phi_result")
phi.add_incoming(value_from_if, if_block)
phi.add_incoming(value_from_else, else_block)
# ❌ 間違い: else_blockからの値を忘れた
phi = builder.phi(i64, name="phi_result")
phi.add_incoming(value_from_if, if_block)
# → Verifierエラー
```
---
### **4. Verifierを毎回実行** ✅
開発中は関数を1つ生成するたびに検証
```python
def build_function(func_name, ...):
# 関数生成
func = ir.Function(module, func_type, name=func_name)
# ... IRを生成 ...
# 必ず検証!
if not func.verify():
raise CompilerError(f"Invalid IR in {func_name}")
return func
```
---
## 📊 **期待される効果(ベンチマーク予測)**
| 最適化手法 | 期待される効果 |
|-----------|-------------|
| LLVM Intrinsics | メモリ操作: 5-10倍高速化 |
| Box構造明示 | メソッド呼び出し: 10-100倍高速化 |
| TBAA | ループ内load削減: 20-50% |
| 関数属性 | 関数呼び出しオーバーヘッド: 50-90%削減 |
**総合効果**: 現在比で **2-5倍の性能向上**を予測
---
## 🎯 **実装優先順位**
1. **Verifier統合** 🔴 最優先
2. **UBチェック** 🔴 最優先
3. **データレイアウト標準化** 🟡 重要
4. **Box構造明示** 🟡 重要
5. **LLVM Intrinsics** 🟢 中
6. **関数属性** 🟢 中
7. **TBAA** 🔵 低(効果大だが実装複雑)
---
## 📚 **参考資料**
- [LLVM Language Reference Manual](https://llvm.org/docs/LangRef.html)
- [LLVM Alias Analysis Infrastructure](https://llvm.org/docs/AliasAnalysis.html)
- [LLVM Function Attributes](https://llvm.org/docs/LangRef.html#function-attributes)
- [LLVM Intrinsics](https://llvm.org/docs/LangRef.html#intrinsic-functions)
---
**まとめ**: Geminiの提案は**実用的で効果的**Phase 21で段階的に実装していく価値がある 🎊