# 帳票設計書 4-コードダンプ (codedump)

## 概要

mrubyの中間表現(IREP)の命令列(オペコード)を人間が読めるアセンブリ形式でstdoutに出力する帳票設計書である。各命令の行番号・オペコード名・オペランドを表示し、デバッグやコンパイル結果の確認に使用される。

### 本帳票の処理概要

本帳票は、mrubyコンパイラまたはインタプリタのverboseモード時に、コンパイル結果のIREPをアセンブリ風のテキスト形式で標準出力にダンプする機能である。各IREP（関数・ブロック単位）について、ヘッダー情報（レジスタ数、ローカル変数数、プール数等）、ローカル変数名、キャッチハンドラ情報、そして各命令の行番号・アドレス・オペコード名・オペランドを逐次出力する。

**業務上の目的・背景**：mrubyのコンパイラ開発者やアプリケーション開発者が、Rubyスクリプトのコンパイル結果を確認し、最適化の状況を把握したり、期待通りのバイトコードが生成されているかを検証するために使用される。mruby VMの命令セットを理解する上での学習ツールとしても有用である。

**帳票の利用シーン**：mrubyコマンドまたはmrbcコマンドに`-v`（verbose）オプションを付けて実行する場面で使用される。例: `mruby -v script.rb` や `mrbc -v script.rb`。

**主要な出力内容**：
1. IREP情報ヘッダー（アドレス、nregs、nlocals、pools数、syms数、reps数、ilen）
2. ローカル変数名一覧（レジスタ番号とシンボル名の対応）
3. キャッチハンドラ情報（rescue/ensure の範囲とジャンプ先）
4. 各命令のダンプ（行番号、アドレス、オペコード名、オペランド、ローカル変数注釈）
5. 子IREPの再帰的ダンプ

**帳票の出力タイミング**：mruby/mrbcコマンドの`-v`（verbose）オプション指定時に自動的に出力される。

**帳票の利用者**：mrubyコンパイラ/VM開発者、mrubyアプリケーションデバッグ担当者、mruby学習者。

## 帳票種別

テキスト出力（stdoutへのアセンブリ風ダンプ）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | CLIコマンド (mruby) | `mruby -v script.rb` | mrubyコマンド実行（-vオプション） |
| - | CLIコマンド (mrbc) | `mrbc -v script.rb` | mrbcコマンド実行（-vオプション） |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（stdout / FILE*） |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A（標準出力） |
| 出力方法 | fprintf / fputs（stdoutまたは指定FILE*） |
| 文字コード | UTF-8 / ASCII |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

各IREPごとに、ヘッダー情報・ローカル変数名・キャッチハンドラ・命令列が順番に出力される。子IREPは親IREPの後に再帰的に出力される。

```
┌─────────────────────────────────────────────────────┐
│ irep 0xXXXX nregs=N nlocals=N pools=N syms=N ...   │
├─────────────────────────────────────────────────────┤
│ local variable names:                                │
│   R1:var1  R2:var2  ...                              │
├─────────────────────────────────────────────────────┤
│ catch type: rescue  begin: 0005 end: 0010 target: 0015 │
├─────────────────────────────────────────────────────┤
│ file: script.rb                                      │
│   1 000 LOADI_1  R1  (1)  ;R1:var1                  │
│   2 001 SEND     R1  :foo  n=0                       │
│   3 002 RETURN   R1                                  │
│                                                      │
├─────────────────────────────────────────────────────┤
│ (子IREPのダンプが続く)                                │
└─────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | IREPアドレス | IREPのメモリアドレス | `irep` ポインタ | `irep %p` |
| 2 | nregs | レジスタ変数数 | `irep->nregs` | `nregs=%d` |
| 3 | nlocals | ローカル変数数 | `irep->nlocals` | `nlocals=%d` |
| 4 | pools | リテラルプール数 | `irep->plen` | `pools=%d` |
| 5 | syms | シンボル数 | `irep->slen` | `syms=%d` |
| 6 | reps | 子IREP数 | `irep->rlen` | `reps=%d` |
| 7 | ilen | 命令数 | `irep->ilen` | `ilen=%d` |

### 明細部

#### 命令行の出力フォーマット

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | 行番号 | ソースコードの行番号 | `mrb_debug_get_line()` | `%5d ` (5桁右詰) | 6 |
| 2 | アドレス | 命令のアドレス（iseq内オフセット） | `pc - irep->iseq` | `%03d ` (3桁ゼロ埋め) | 4 |
| 3 | オペコード名 | 命令名 | `switch (ins)` | タブ区切りテキスト | 可変 |
| 4 | オペランド | 命令の引数 | FETCH_*マクロ | `R%d`, `L[%d]`, `:%s`等 | 可変 |
| 5 | ローカル変数注釈 | レジスタに対応する変数名 | `irep->lv` | `; R%d:%s` | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 空行 | IREP間の区切り | - | `\n` |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 入力ファイル | コンパイル対象のRubyスクリプトファイル(.rb) | Yes |
| -v フラグ | verboseモードの有効化 | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 命令アドレス順（iseq内のオフセット順） | 昇順 |
| 2 | IREP階層（親→子の再帰順） | 昇順 |

### 改ページ条件

N/A（テキスト出力のため改ページなし。IREP間に空行を挿入）

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを参照しない。インメモリのIREPデータ構造体を参照する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| mrb_irep (構造体) | IREP本体データ | コンパイル結果から直接取得 |
| mrb_irep_debug_info (構造体) | 行番号・ファイル名情報 | `irep->debug_info` |

### テーブル別参照項目詳細

#### mrb_irep

| 参照項目（フィールド名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| nlocals | ヘッダーのnlocals | 常時 | - |
| nregs | ヘッダーのnregs | 常時 | - |
| plen | ヘッダーのpools | 常時 | - |
| slen | ヘッダーのsyms | 常時 | - |
| rlen | ヘッダーのreps | 常時 | - |
| ilen | ヘッダーのilen | 常時 | - |
| iseq | 命令列（pcとして走査） | 常時 | - |
| lv | ローカル変数名（注釈表示） | lv != NULL | - |
| pool | リテラルプール値（LOADL等の注釈） | 命令参照時 | - |
| syms | シンボル名（SEND, GETIV等の注釈） | 命令参照時 | - |
| clen | キャッチハンドラ数 | clen > 0 | - |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 命令アドレス | `pc - irep->iseq` | なし | ptrdiff_t |
| ジャンプ先アドレス | `(pc - irep->iseq) + (int16_t)operand` | なし | JMP系命令のターゲット |
| 引数解析(n) | `operand & 0xf` | なし | メソッド呼び出しの引数数 |
| 引数解析(nk) | `(operand >> 4) & 0xf` | なし | キーワード引数数 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[mruby/mrbc -v 実行] --> B[コンパイル完了]
    B --> C[mrb_codedump_all呼び出し]
    C --> D[codedump_recur呼び出し]
    D --> E[codedump: IREPヘッダー出力]
    E --> F{ローカル変数あり?}
    F -->|Yes| G[ローカル変数名一覧出力]
    F -->|No| H{キャッチハンドラあり?}
    G --> H
    H -->|Yes| I[キャッチハンドラ情報出力]
    H -->|No| J[命令列の走査開始]
    I --> J
    J --> K{pc < pcend?}
    K -->|Yes| L[ファイル名変更チェック・出力]
    L --> M[行番号・アドレスヘッダー出力]
    M --> N[オペコード読み込み・switch分岐]
    N --> O[オペコード名・オペランド出力]
    O --> P[ローカル変数注釈出力]
    P --> K
    K -->|No| Q{子IREPあり?}
    Q -->|Yes| R[子IREPで再帰呼び出し]
    R --> Q
    Q -->|No| S[fflush・完了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| IREPがNULL | irep==NULLの場合 | 出力なし（即return） | コンパイル処理の確認 |
| 不明オペコード | switch文のdefaultに到達 | `unknown_op (0x%x)` | mrubyバージョンの確認 |
| 行番号取得失敗 | mrb_debug_get_lineが負の値を返す | 6文字分の空白 | デバッグ情報の有無を確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1IREPあたり数十〜数千命令 |
| 目標出力時間 | ミリ秒単位（fprintf逐次出力） |
| 同時出力数上限 | 1 |

## セキュリティ考慮事項

- 出力にはシンボル名、文字列リテラル、数値リテラルが含まれるため、ソースコードの構造が推測可能
- 本番環境での使用は推奨されない（デバッグ・開発用途）
- MRB_NO_STDIOが定義されている場合、codedump関数は除外される（mrb_codedump_allは空関数となる）

## 備考

- OP_EXT1/EXT2/EXT3はオペランド拡張命令で、次の命令のオペランドサイズを変更する
- print_args関数はメソッド呼び出しの引数情報（n=通常引数数, nk=キーワード引数数）を表示する。n=15は可変長引数(*)、nk=15はキーワード可変長(**)を示す
- ローカル変数注釈は、レジスタ番号に対応するローカル変数名がある場合にセミコロン後に表示される
- mrb_codedump_allはstdoutに出力、mrb_codedump_all_fileは任意のFILE*に出力可能

---

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

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | irep.h | `include/mruby/irep.h` | `struct mrb_irep`（55-80行目）: iseq, pool, syms, lv, debug_infoのフィールド |
| 1-2 | opcode.h | `include/mruby/opcode.h` | オペコード定義: OP_NOP, OP_MOVE, OP_SEND等の命令コード |
| 1-3 | irep.h | `include/mruby/irep.h` | `struct mrb_irep_catch_handler`（47-52行目）: キャッチハンドラ構造体 |

**読解のコツ**: CASEマクロ（93行目）が`case insn: FETCH_ops(); L_insn`に展開される。FETCH_*マクロはオペコードごとのオペランド読み出しパターンを定義する（Z=引数なし, B=1バイト, BB=2バイト, BBB=3バイト等）。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | codedump.c | `src/codedump.c` | `mrb_codedump_all`（687-692行目）: 公開APIエントリーポイント。stdoutに出力 |
| 2-2 | codedump.c | `src/codedump.c` | `mrb_codedump_all_file`（678-682行目）: FILE*指定版。codedump_recurを呼び出し |
| 2-3 | codedump.c | `src/codedump.c` | `codedump_recur`（667-675行目）: codedump→子IREP再帰の制御 |

**主要処理フロー**:
1. **690行目**: mrb_codedump_allがmrb_codedump_all_fileをstdoutで呼び出し
2. **680行目**: codedump_recurを呼び出し
3. **669行目**: codedumpで現在のIREPをダンプ
4. **671-673行目**: 子IREPを再帰的にダンプ

#### Step 3: コアダンプ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | codedump.c | `src/codedump.c` | `codedump`関数（96-664行目）: メインのダンプ処理 |
| 3-2 | codedump.c | `src/codedump.c` | 101-102行目: IREPヘッダー出力（nregs, nlocals, pools等） |
| 3-3 | codedump.c | `src/codedump.c` | 104-117行目: ローカル変数名一覧出力 |
| 3-4 | codedump.c | `src/codedump.c` | 119-144行目: キャッチハンドラ情報出力 |
| 3-5 | codedump.c | `src/codedump.c` | 148-662行目: 命令ループ（while pc < pcend） |

**主要処理フロー**:
- **101行目**: IREPヘッダー情報をfprintfで出力
- **107-116行目**: 各ローカル変数のレジスタ番号と名前を出力
- **122-143行目**: キャッチハンドラのtype/begin/end/targetを出力
- **157-161行目**: ファイル名変更の検出・出力
- **162行目**: print_headerで行番号とアドレスを出力
- **163-660行目**: オペコードごとのswitch文で命令をテキスト化

#### Step 4: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | codedump.c | `src/codedump.c` | `print_header`（52-66行目）: 行番号とアドレスの出力 |
| 4-2 | codedump.c | `src/codedump.c` | `print_lv_a`（31-38行目）: 1レジスタのローカル変数注釈 |
| 4-3 | codedump.c | `src/codedump.c` | `print_lv_ab`（41-49行目）: 2レジスタのローカル変数注釈 |
| 4-4 | codedump.c | `src/codedump.c` | `print_args`（69-91行目）: メソッド呼び出し引数数の出力 |
| 4-5 | codedump.c | `src/codedump.c` | `print_r`（22-28行目）: レジスタ番号と変数名の出力 |

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

```
mrb_codedump_all() [codedump.c:687]
    │
    └─ mrb_codedump_all_file() [codedump.c:678]
           │
           └─ codedump_recur() [codedump.c:667]  ← 再帰
                  │
                  ├─ codedump() [codedump.c:96]
                  │      │
                  │      ├─ fprintf() ← IREPヘッダー出力 [101行目]
                  │      ├─ mrb_sym_dump() ← ローカル変数名表示 [108行目]
                  │      ├─ mrb_irep_catch_handler_table() ← キャッチハンドラ [120行目]
                  │      │
                  │      └─ (命令ループ while pc < pcend)
                  │             ├─ mrb_debug_get_filename() [157行目]
                  │             ├─ print_header() [codedump.c:52]
                  │             │      └─ mrb_debug_get_line() [57行目]
                  │             ├─ READ_B() ← オペコード読み出し
                  │             ├─ (switch文: 各オペコード処理)
                  │             ├─ print_lv_a() / print_lv_ab() ← 変数注釈
                  │             │      └─ print_r() [codedump.c:22]
                  │             └─ print_args() [codedump.c:69]
                  │
                  └─ codedump_recur() ← 子IREP再帰 [672行目]
```

### データフロー図

```
[入力]                     [処理]                           [出力]

mrb_irep ─────────▶ codedump() ───────────────▶ stdout / FILE*
    │                  │                            │
    ├─ nregs/nlocals   ├─ IREPヘッダー出力           │
    ├─ lv[]            ├─ ローカル変数名出力          │
    ├─ iseq            ├─ キャッチハンドラ出力        │
    ├─ pool[]          ├─ 各命令のダンプ             │
    ├─ syms[]          │   ├─ 行番号(debug_info)    │
    ├─ debug_info      │   ├─ アドレス              │
    └─ clen            │   ├─ オペコード名           │
                       │   ├─ オペランド             │
                       │   └─ ローカル変数注釈       │
                       └─ 子IREP再帰               │
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| codedump.c | `src/codedump.c` | ソース | コードダンプのコア実装。codedump, codedump_recur, mrb_codedump_all等 |
| irep.h | `include/mruby/irep.h` | ヘッダー | mrb_irep構造体定義 |
| opcode.h | `include/mruby/opcode.h` | ヘッダー | オペコード定義 |
| ops.h | `include/mruby/ops.h` | ヘッダー | OPCODE/FETCHマクロ展開用（EXT1/2/3処理） |
| debug.h | `include/mruby/debug.h` | ヘッダー | mrb_debug_get_line, mrb_debug_get_filename関数宣言 |
| proc.h | `include/mruby/proc.h` | ヘッダー | RProc構造体定義（body.irep参照） |
