# 機能設計書：llvm-split

## 1. 機能概要

### 1.1 機能名
llvm-split - LLVMモジュールを分割するツール

### 1.2 機能説明
llvm-splitは、1つのLLVM IRモジュールを複数のモジュールに分割するツールです。`llvm::SplitModule`および`TargetMachine::splitModule`関数のテストに使用されます。分散コンパイルやThinLTOの並列処理のためのモジュール分割をサポートします。

### 1.3 関連する画面/API
- CLI（コマンドラインインターフェース）
- 入力：.bcファイル（ビットコード）または.llファイル（LLVM IR）
- 出力：複数の分割されたモジュールファイル

---

## 2. 入出力設計

### 2.1 入力
| 項目 | 型 | 必須 | 説明 |
|------|-----|------|------|
| input file | ファイルパス | No | 入力ビットコードファイル（デフォルト: 標準入力 "-"） |
| -o | ファイルパス | No | 出力ファイル名のベース |
| -j | 数値 | No | 出力ファイル数（デフォルト: 2） |
| -mtriple | 文字列 | No | ターゲットトリプル |
| -mcpu | 文字列 | No | ターゲットCPU |

### 2.2 出力
| 項目 | 型 | 説明 |
|------|-----|------|
| output files | ファイル群 | 分割されたビットコードまたはアセンブリファイル |
| exit code | 整数 | 0: 成功、1: エラー |

### 2.3 コマンドラインオプション
| オプション | 説明 |
|-----------|------|
| -o | 出力ファイル名のベースを指定 |
| -j<N> | 出力ファイル数を指定（デフォルト: 2） |
| -preserve-locals | ローカルシンボルを外部化せずに分割 |
| -round-robin | 名前ハッシュの代わりにラウンドロビン分配を使用 |
| -mtriple | ターゲットトリプル指定（TargetMachine::splitModule使用） |
| -mcpu | ターゲットCPU指定（-mtriple使用時のみ有効） |
| -split-by-category | カテゴリ別分割（module-id / kernel） |
| -S | LLVM IRアセンブリとして出力 |

---

## 3. 処理フロー

### 3.1 基本フロー

```
[開始]
   │
   ▼
[コマンドライン引数解析]
   │
   ▼
[ターゲットマシン初期化（-mtriple指定時）]
   │
   ▼
[入力モジュール読み込み] ←─── parseIRFile()
   │
   ▼
[分割方式選択]
   │
   ├─► -split-by-category指定 ──► runSplitModuleByCategory()
   │
   ├─► -mtriple指定 ──► TargetMachine::splitModule()
   │                         │
   │                    失敗時 ▼
   │              [フォールバックへ]
   │
   └─► デフォルト ──► SplitModule()
           │
           ▼
   [各分割モジュールを出力]
           │
           ▼
        [終了]
```

### 3.2 カテゴリ別分割処理

```
runSplitModuleByCategory()
   │
   ▼
[EntryPointCategorizer作成]
   │
   ▼
[splitModuleTransitiveFromEntryPoints()]
   │
   ▼
[各分割モジュールに対して]
   │
   ├─► verifyModule() ──► 検証
   │
   ├─► cleanupModule() ──► GlobalDCEパス実行
   │
   └─► writeModuleToFile() ──► ファイル出力
```

### 3.3 カテゴリ判定処理

```
EntryPointCategorizer()
   │
   ▼
[関数がエントリポイント?]
   │
   ├─► No ──► std::nulloptを返す（スキップ）
   │
   └─► Yes
         │
         ▼
   [カテゴリタイプ判定]
         │
         ├─► SBCT_ByKernel ──► 関数名をキーに
         │
         └─► SBCT_ByModuleId ──► module-id属性をキーに
               │
               ▼
         [カテゴリIDを返す]
```

---

## 4. データ構造

### 4.1 カテゴリ分割タイプ
```cpp
enum class SplitByCategoryType {
    SBCT_ByModuleId,   // module-id属性でグループ化
    SBCT_ByKernel,     // カーネル関数ごとに分割
    SBCT_None          // カテゴリ分割なし
};
```

### 4.2 EntryPointCategorizer
```cpp
class EntryPointCategorizer {
    SplitByCategoryType Type;
    DenseMap<SmallString<0>, int, KeyInfo> StrKeyToID;

    // エントリポイント判定
    static bool isEntryPoint(const Function &F);

    // カテゴリ文字列計算
    static SmallString<0> computeFunctionCategory(
        SplitByCategoryType Type, const Function &F);
};
```

### 4.3 エントリポイント条件
- 宣言でない（定義がある）
- 以下のいずれかのカーネル呼び出し規約:
  - `CallingConv::SPIR_KERNEL`
  - `CallingConv::AMDGPU_KERNEL`
  - `CallingConv::PTX_Kernel`

---

## 5. 分割アルゴリズム

### 5.1 デフォルト分割（SplitModule）
- 関数名のハッシュ値を使用
- `-round-robin`指定時はラウンドロビン分配
- `-preserve-locals`でローカルシンボルの外部化を抑制

### 5.2 ターゲット固有分割（TargetMachine::splitModule）
- ターゲットマシン固有の分割ロジック
- 失敗時はデフォルト分割にフォールバック
- `-preserve-locals`と`-round-robin`は無効

### 5.3 カテゴリ別分割（splitModuleTransitiveFromEntryPoints）
- カーネル関数を基点とした推移的分割
- module-id属性または関数名でグループ化
- GlobalDCEパスで不要コード削除

---

## 6. エラーハンドリング

### 6.1 エラーケース
| エラー | 原因 | 対応 |
|--------|------|------|
| 入力ファイル解析失敗 | 不正なIRファイル | エラーメッセージ出力、終了コード1 |
| ターゲット未検出 | 無効なターゲットトリプル | エラーメッセージ出力、終了コード1 |
| 出力ファイルオープン失敗 | パーミッション等 | エラーメッセージ出力、exit(1) |
| モジュール検証失敗 | 分割結果が不正 | "Broken module!"出力、exit(1) |

### 6.2 警告
| 警告 | 状況 |
|------|------|
| --preserve-locals無効 | TargetMachine::splitModule使用時 |
| --round-robin無効 | TargetMachine::splitModule使用時 |
| フォールバック | TargetMachine::splitModule失敗時 |

---

## 7. 技術仕様

### 7.1 出力ファイル命名規則
- 標準分割: `{OutputFilename}{N}` (N: 0, 1, 2, ...)
- カテゴリ分割: `{OutputFilename}_{N}.ll` または `{OutputFilename}_{N}.bc`

### 7.2 モジュールクリーンアップ
```cpp
void cleanupModule(Module &M) {
    ModulePassManager MPM;
    MPM.addPass(GlobalDCEPass()); // 到達不能グローバル削除
    MPM.run(M, MAM);
}
```

### 7.3 ターゲット初期化
- `-mtriple`指定時のみターゲットを初期化
- `InitializeAllTargets()`と`InitializeAllTargetMCs()`を呼び出し

---

## 8. 制約事項

### 8.1 入力制約
- 有効なLLVM IRまたはビットコードファイルが必要
- カテゴリ分割はカーネル呼び出し規約を持つ関数のみ対象

### 8.2 処理制約
- TargetMachine::splitModule使用時は一部オプションが無効
- カテゴリ分割はGlobalDCEパスが必須

---

## 9. 非機能要件

### 9.1 パフォーマンス
- 関数数に比例した処理時間
- メモリ使用量はモジュールサイズに依存

### 9.2 互換性
- 複数のGPUアーキテクチャ（SPIR、AMDGPU、PTX）に対応
- 汎用SplitModuleとターゲット固有分割の両方をサポート

---

## 10. コードリーディングガイド

### 10.1 エントリーポイント
- **ファイル**: `llvm/tools/llvm-split/llvm-split.cpp`
- **関数**: `main()` (238-323行目)

### 10.2 重要な関数
| 関数名 | 行番号 | 説明 |
|--------|--------|------|
| main() | 238-323 | エントリーポイント、全体制御 |
| writeModuleToFile() | 113-126 | モジュールをファイルに出力 |
| writeStringToFile() | 101-111 | 文字列をファイルに出力 |
| cleanupModule() | 205-211 | モジュールのDCE実行 |
| runSplitModuleByCategory() | 213-236 | カテゴリ別分割実行 |
| EntryPointCategorizer::operator() | 146-156 | カテゴリ判定 |
| EntryPointCategorizer::isEntryPoint() | 159-166 | エントリポイント判定 |
| EntryPointCategorizer::computeFunctionCategory() | 168-184 | カテゴリ文字列計算 |

### 10.3 プログラム呼び出し階層図

```
main()
├── cl::ParseCommandLineOptions()           [244行目]
├── [TargetMachine初期化]                   [249-263行目]
│   ├── InitializeAllTargets()              [250行目]
│   ├── InitializeAllTargetMCs()            [251行目]
│   ├── TargetRegistry::lookupTarget()      [254行目]
│   └── T->createTargetMachine()            [261-262行目]
├── parseIRFile()                           [265行目]
├── [分割方式選択]
│   ├── runSplitModuleByCategory()          [294行目]
│   │   ├── EntryPointCategorizer()         [232行目]
│   │   ├── splitModuleTransitiveFromEntryPoints() [233-234行目]
│   │   ├── verifyModule()                  [216行目]
│   │   ├── cleanupModule()                 [223行目]
│   │   │   └── GlobalDCEPass()             [209行目]
│   │   └── writeModuleToFile()             [229行目]
│   ├── TM->splitModule()                   [313行目]
│   └── SplitModule()                       [321行目]
└── HandleModulePart() lambda               [273-291行目]
    ├── verifyModule()                      [282行目]
    └── WriteBitcodeToFile()                [287行目]
```

### 10.4 データフロー図

```
入力ファイル (.bc/.ll)
         │
         ▼
    ┌─────────────┐
    │ parseIRFile │
    └─────────────┘
         │
         ▼ Module
    ┌─────────────────────┐
    │   分割方式選択       │
    │                     │
    │ ├─ カテゴリ分割     │
    │ │  EntryPointCategorizer
    │ │  splitModuleTransitiveFromEntryPoints
    │ │                   │
    │ ├─ ターゲット分割   │
    │ │  TargetMachine::splitModule
    │ │                   │
    │ └─ デフォルト分割   │
    │    SplitModule      │
    └─────────────────────┘
         │
         ▼ 複数のModule
    ┌─────────────┐
    │ verifyModule│ ← 検証
    └─────────────┘
         │
         ▼
    ┌─────────────────────┐
    │ cleanupModule       │ ← カテゴリ分割時のみ
    │ (GlobalDCEPass)     │
    └─────────────────────┘
         │
         ▼
    ┌─────────────────────┐
    │ writeModuleToFile   │
    │ WriteBitcodeToFile  │
    │ print()             │
    └─────────────────────┘
         │
         ▼
      出力ファイル群
      (file0, file1, file2, ...)
```

### 10.5 関連ファイル一覧

| ファイルパス | 役割 |
|-------------|------|
| llvm/tools/llvm-split/llvm-split.cpp | メインソースファイル（324行） |
| llvm/include/llvm/Transforms/Utils/SplitModule.h | SplitModule関数定義 |
| llvm/include/llvm/Transforms/Utils/SplitModuleByCategory.h | カテゴリ別分割関数定義 |
| llvm/include/llvm/Transforms/IPO/GlobalDCE.h | GlobalDCEパス定義 |
| llvm/include/llvm/Target/TargetMachine.h | TargetMachineクラス定義 |
| llvm/include/llvm/Bitcode/BitcodeWriter.h | ビットコード書き込みAPI |
| llvm/include/llvm/IRReader/IRReader.h | IR読み込みAPI |

---

## 11. 使用例

### 11.1 基本的な分割（2分割）
```bash
llvm-split input.bc -o output
# output0, output1 が生成される
```

### 11.2 4分割
```bash
llvm-split input.bc -j4 -o output
# output0, output1, output2, output3 が生成される
```

### 11.3 ラウンドロビン分配
```bash
llvm-split input.bc -j3 -round-robin -o output
```

### 11.4 ローカルシンボル保持
```bash
llvm-split input.bc -preserve-locals -o output
```

### 11.5 ターゲット固有分割
```bash
llvm-split input.bc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -j4 -o output
```

### 11.6 カーネル別分割
```bash
llvm-split input.bc -split-by-category=kernel -o output
```

### 11.7 モジュールID別分割
```bash
llvm-split input.bc -split-by-category=module-id -o output
```

### 11.8 アセンブリ出力
```bash
llvm-split input.bc -S -o output
# output_0.ll, output_1.ll が生成される
```

---

## 12. 改訂履歴

| 版数 | 日付 | 変更内容 | 担当者 |
|------|------|----------|--------|
| 1.0 | 2026-02-01 | 初版作成 | llvm-split機能設計書生成 |
