Status: Active  
Date: 2025-12-22  
Scope: Pattern6/7 を `Frag + emit_frag()` へ段階吸収（pattern列挙の増殖を止める）  
Related:
- Design SSOT: `docs/development/current/main/design/edgecfg-fragments.md`
- Phase 269（Pattern8 Frag）: `docs/development/current/main/phases/phase-269/README.md`
- Phase 270（Pattern9 bridge）: `docs/development/current/main/phases/phase-270/README.md`

# Phase 272（P0）: Pattern6/7 を Frag+emit_frag へ吸収（段階適用）

## ステータス

- **P0.1（Pattern6）**: ✅ 完了（Frag+emit_frag 経路へ移行）
- **P0.2（Pattern7）**: ✅ 完了（Frag+emit_frag 経路へ移行）

## 目的

- Pattern6/7（scan系）の CFG 構築を “pattern番号ごとの推測分岐” から外し、**EdgeCFG Frag 合成（ExitKind/wires/branches）**に収束させる。
- terminator emission を SSOT（`emit_frag()`）へ集約し、block の successors/preds 同期漏れを構造で防ぐ。

## スコープ境界

### ✅ 触る
- `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
- `src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs`
- `src/mir/builder/emission/`（Pattern8 と同じ “薄い入口” の追加）

### ❌ 触らない
- merge/EdgeCFG plumbing（Phase 260-268 の SSOT は維持）
- cf_loop の非JoinIR経路追加（JoinIR-only hard-freeze 維持）
- by-name ハードコード（Box名/Pattern名文字列での分岐増殖など）

## 入口SSOT（fixture/smoke）

### Pattern6（index_of）
- fixture: `apps/tests/phase254_p0_index_of_min.hako`（exit=1）
- smoke (VM): `tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`

### Pattern7（split）
- fixture: `apps/tests/phase256_p0_split_min.hako`（exit=3）
- smoke (VM): `tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`

## 方針（P0）

P0 は “両方一気に” ではなく、以下の順で段階適用する。

1. Pattern6 を `Frag + emit_frag()` に切り替え（wiring を SSOT 化）
2. Pattern7 を `Frag + emit_frag()` に切り替え（副作用 push を含む）
3. 旧 JoinIR 経路の撤去条件が満たせた時点で削除（本READMEに明記）

## 実装ガイド（共通）

- PHI は block 先頭（after existing phis）へ挿入し、入力を `[(pred_bb, val)]` の形で固定する:
  - `crate::mir::ssot::cf_common::insert_phi_at_head_spanned`
- terminator emission は `crate::mir::builder::control_flow::edgecfg::api::emit_frag` に集約する。
- Pattern8 の構造（参考）:
  - emission 入口: `src/mir/builder/emission/loop_predicate_scan.rs`

## P0.1: Pattern6（index_of）— Frag 化

### 狙い
- loop 骨格（header/body/step/after + early return）を Frag に落とし、Jump/Branch/Return を `emit_frag()` に集約する。

### 実装結果（✅ 完了）

- emission 入口を新設し、Pattern6 の terminator emission を `emit_frag()`（SSOT）へ集約
  - 新規: `src/mir/builder/emission/loop_scan_with_init.rs`
  - 更新: `src/mir/builder/emission/mod.rs`
- Pattern6 の JoinIRConversionPipeline 経路を撤去し、Frag 経路へ切り替え
  - 更新: `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
- P0 スコープ:
  - forward scan（`step=1`）のみ適用
  - reverse/dynamic needle 等は `Ok(None)` で不適用（既定挙動不変）
- 旧 DCE 対策（exit PHI 用の post-loop guard）を撤去（Frag が Return を直接 emit するため）

### 最小 CFG 形（forward scan）
- blocks: `header`, `body`, `step`, `after`, `ret_found`
- header:
  - `i_current = phi [i_init, preheader], [i_next, step_bb]`
  - `cond_loop = (i_current < len)`
  - branch: true→body, false→after
- body:
  - `ch = s.substring(i_current, i_current+1)`
  - `cond_match = (ch == needle)`
  - branch: true→ret_found, false→step
- step:
  - `i_next = i_current + 1`
  - jump: →header
- ret_found:
  - wire: `Return(i_current)`
- after:
  - P0 では `return -1` を既存 AST lowering に任せてもよい（after を current_block にする）

### 受け入れ
- `cargo test -p nyash-rust --lib --release`
- `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`

### 追加の検証（推奨）

- `NYASH_VM_DUMP_MIR=1 ./target/release/hakorune --backend vm apps/tests/phase254_p0_index_of_min.hako` で PHI/terminator を確認（任意）

## P0.2: Pattern7（split）— Frag 化

### 狙い
- Pattern7 の terminator 配線（if/loop の遷移）を Frag に集約し、副作用（`result.push`）を含む形でも CFG を壊さない。

### 注意点（Fail-Fast）
- carriers が複数（`i`, `start`）なので、header の PHI を 2 本（以上）で SSA を閉じる必要がある。
- `result.push` は副作用なので、block 配置（評価順）を壊さない（P0は固定形のみ受理）。

### 実装結果（✅ 完了）

- Pattern7 の JoinIRConversionPipeline 経路を撤去し、Frag+emit_frag 経路へ切り替え
  - 更新: `src/mir/builder/control_flow/joinir/patterns/pattern7_split_scan.rs`
- emission 入口を新設し、terminator emission を `emit_frag()`（SSOT）へ集約
  - 新規: `src/mir/builder/emission/loop_split_scan.rs`
  - 更新: `src/mir/builder/emission/mod.rs`
- CFG 形（P0）:
  - blocks: `header`, `body`, `then`, `else`, `step`, `after`（+ 入口 preheader）
  - header: PHI（`i_current`, `start_current`） + loop condition
  - body: delimiter match check
  - then: `result.push(segment)` + `start_next_then` 計算（dominance 安全）
  - else: `i_next_else = i_current + 1`
  - step: PHI（`i_next`, `start_next`） + jump header
- Compare は `CompareOp::Le`（`i <= limit`）を使用（固定形）

### リファクタ結果（共通SSOT）

Phase 272 P0.2 完了後、Pattern6/7/8 の重複を以下へ収束した（仕様不変）:

- PHI 挿入の薄いラッパ: `src/mir/builder/emission/phi.rs`
- variable_map の fail-fast 取得: `src/mir/builder/variable_context.rs`（`require(name, ctx)`）
- can_lower の戦略メモ: `src/mir/builder/control_flow/joinir/patterns/router.rs`（`CanLowerStrategy`）

### 受け入れ
- `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`
  - PASS（exit=3）
  - MIR 形（PHI/Branch/Jump/BoxCall(push)）を目視確認できること（任意）

## Next（planned）: Phase 273（design-first）— Pattern を Plan Extractor に降格して裾広がりを止める

Phase 272（P0）で “terminator SSOT（emit_frag）へ寄せる” を完了したら、次は上流の収束（compiler flow の一本化）を行う。

- 相談メモ（外部レビュー用）: `docs/development/current/main/investigations/phase-272-frag-plan-architecture-consult.md`
- ねらい:
  - Pattern = **Plan 抽出（pure）** に降格（builder を触らない）
  - Plan = `seq/if/loop/exit/effect/let` の固定語彙（増殖しない）
  - PlanLowerer が block/value/phi を作る唯一の箱（emit_frag は SSOT のまま）
- 受け入れ（最小）:
  - extractor が `next_block_id/next_value_id/insert_phi_*` を呼ばない（純関数）
  - Plan→Frag→emit_frag の本線が 1 本になる（pattern番号列挙を中心にしない）

## 旧 JoinIR 経路の撤去条件（SSOT）

旧 `JoinIRConversionPipeline` 系の経路を削るのは、以下を満たした後に行う。

1. Pattern6/7 の fixture/smoke が Frag 経路で PASS
2. `tools/smokes/v2/run.sh --profile quick` が悪化しない
3. router から該当 pattern の “旧経路” が消せる（最小差分で削除可能）

## テスト手順（固定）

1. `cargo build --release`
2. `cargo test -p nyash-rust --lib --release`
3. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh`
4. `HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase256_p0_split_vm.sh`
