diff --git a/docs/development/current/main/phases/phase-282/README.md b/docs/development/current/main/phases/phase-282/README.md new file mode 100644 index 00000000..39e8c087 --- /dev/null +++ b/docs/development/current/main/phases/phase-282/README.md @@ -0,0 +1,129 @@ +# Phase 282: Router Shrinkage (pattern番号の症状ラベル化) + +Status: Planned (design-first) + +Goal: +- pattern番号(Pattern1/2/…)を “症状ラベル(テスト名)” に縮退させ、router の責務を「抽出の配線」へ収束させる。 +- CFG 構築は `Frag/ExitKind` 合成 SSOT(`compose::*` + `emit_frag()`)へ一本化する。 + +## SSOT References + +- Frag/emit SSOT: `docs/development/current/main/design/edgecfg-fragments.md` +- Composition SSOT: `src/mir/builder/control_flow/edgecfg/api/compose.rs` +- JoinIR overview: `docs/development/current/main/joinir-architecture-overview.md` +- Plan line(Pattern6/7): `docs/development/current/main/phases/phase-273/README.md` +- Composition adoption(Pattern6/7): `docs/development/current/main/phases/phase-281/README.md` + +## Problem + +pattern番号が router の分岐点として肥大化すると、以下が起きる: +- “認識(extract)” と “CFG構築(lower)” が混ざる +- 分岐の追加が雪だるま式に増える(実質、2つ以上のコンパイラを育てる) +- 収束先(SSOT)が曖昧になり、バグ修正の導線が折れる + +## Target Shape (SSOT) + +router の役割をここまで縮退させる: +- **やる**: Extractor の列挙、Ok(None)/Err の境界管理、Plan/JoinIR の入口選択 +- **やらない**: CFG構築のディテール、terminator 生成、推測 fallback + +CFG構築は以下に収束させる: +- Plan line: `Extractor → DomainPlan → Normalizer → CorePlan → Lowerer → emit_frag()` +- JoinIR line: `cf_loop → (必要な抽出) → Frag composition → emit_frag()` + +## P0 (docs-only) — SSOT 固定 + +- router の責務/禁止事項を明文化(by-name hardcode禁止、silent fallback禁止) +- "pattern番号の意味" を SSOT として定義(テスト名/症状ラベル) +- 入口リンクを `10-Now.md` に固定 + +### Router Responsibilities SSOT + +**Router がやること**: +1. Extraction 戦略の列挙(Plan-based, JoinIR table-based) +2. Extractor を優先順で呼び出し(Plan line → JoinIR table) +3. Ok(None)/Err の境界管理(Fail-Fast原則) +4. Entrypoint の選択(Plan line vs JoinIR line) +5. Routing 決定のログ出力(debug mode のみ、既存 trace 機構) + +**Router がやらないこと**(禁止事項): +1. CFG 構築(block 割り当て、PHI 挿入、terminator 生成) +2. Pattern 固有の lowering ロジック(Normalizer/Lowerer に委譲) +3. Silent fallback(エラーは明示的に) +4. By-name hardcode(関数名マッチング禁止、debug 以外) +5. Mock path fallback(test 専用パターンの本番使用禁止) + +**Pattern 番号 = 症状ラベル**(Phase 280 SSOT positioning): +- ✅ 正しい用途: テスト名(`loop_if_phi.hako` → Pattern3_WithIfPhi)、debug ログ +- ❌ 禁止: CFG 分岐(`if pattern == 6 then ...`)、アーキテクチャ SSOT(Frag composition が SSOT) + +**Detection 戦略**(簡潔版、詳細は P2+ で補完): +- **ExtractionBased**(Pattern6,7,8): extract() 成功 → match(SSOT 単一) +- **StructureBased**(Pattern1-5,9): ctx.pattern_kind チェック(legacy、2 つの SSOT) + +**SSOT 参照**: +- Frag composition: `src/mir/builder/control_flow/edgecfg/api/compose.rs` +- Plan line: `src/mir/builder/control_flow/plan/normalizer.rs` +- Terminator 生成: `emit_frag()` (Phase 267) + +### Ok(None)/Err Boundary Rules + +**Fail-Fast 原則**: +- `Ok(None)`: Extraction がパターンに一致しなかった(次を試す) +- `Err(String)`: Extraction が一致したが検証失敗(即座に fail) +- **禁止**: Close-but-unsupported → Ok(None)(Err で返すべき) + +**例**: + +```rust +// ✅ 正しい: 一致しない +extract_scan_with_init_plan() → Ok(None) // 次のパターンを試す + +// ✅ 正しい: 一致したが非サポート +extract_scan_with_init_plan() → Err("P1 scope: reverse scan not supported") + +// ❌ 禁止: Silent fallback +extract_scan_with_init_plan() → Ok(None) for unsupported cases +// (非サポートケースを Ok(None) で返してはいけない) +``` + +**境界の位置**(router.rs 実装): +- Plan line(lines 310-337, 341-368): extract → match → Normalize → Verify → Lower +- JoinIR table(lines 376-382): can_lower → lower +- No match(lines 384-395): Ok(None) を caller に返す(Err ではない) + +### Entrypoint Table + +| Entrypoint | Entry Condition | Patterns | SSOT Downstream | +|------------|----------------|----------|-----------------| +| **Plan line** | extract_*_plan() が Ok(Some) | Pattern6/7 | Normalizer → CorePlan → Lowerer → emit_frag | +| **JoinIR table** | LOOP_PATTERNS 反復 | Pattern1-5,8-9 | cf_loop 抽出 → Frag composition → emit_frag | +| **No match** | すべての extract が Ok(None) | (none) | Err を caller に返す | + +**Plan line の責務**(Phase 273 SSOT): +1. **Extraction**: extract_*_plan()(pure、builder 不要) +2. **Normalization**: PlanNormalizer(pattern 知識の展開) +3. **Verification**: PlanVerifier(fail-fast 検証) +4. **Lowering**: PlanLowerer(block/value 割り当て + Frag composition) +5. **Emission**: emit_frag()(terminator SSOT) + +**JoinIR table の責務**(Phase 194+ table-driven): +1. **Detection**: can_lower()(structure-based、ctx.pattern_kind) +2. **Extraction**: cf_loop 抽出 +3. **収束先**: Frag composition → emit_frag(内部パイプライン詳細は最小化) + +**収束先の統一**(Phase 282 Goal): +- すべての entrypoint が最終的に `emit_frag()` に収束(terminator 生成の SSOT) +- CFG 構築の詳細は router が持たない(抽出の配線のみ) + +## P1 (code, minimal) — 配線の可視化 + +- router に “経路ログ” を追加(既定OFF、debugのみに限定) +- pattern番号ではなく “entrypoint(Plan/JoinIR)” をログの主語にする + +## Acceptance (Phase 282) + +- router の責務が docs で SSOT 化されている +- router の変更が「extractor配線」のみになっている(CFG構築の詳細を持たない) +- 既存の VM/LLVM smokes に退行がない + diff --git a/src/mir/builder/control_flow/joinir/patterns/router.rs b/src/mir/builder/control_flow/joinir/patterns/router.rs index 173f819e..be66c82f 100644 --- a/src/mir/builder/control_flow/joinir/patterns/router.rs +++ b/src/mir/builder/control_flow/joinir/patterns/router.rs @@ -314,7 +314,7 @@ pub(crate) fn route_loop_pattern( )? { Some(domain_plan) => { // DomainPlan extracted successfully - trace::trace().pattern("route", "Pattern6_ScanWithInit (DomainPlan)", true); + trace::trace().pattern("route", "route=plan pattern=Pattern6_ScanWithInit (Phase 273)", true); // Step 1: Normalize DomainPlan → CorePlan let core_plan = PlanNormalizer::normalize(builder, domain_plan, ctx)?; @@ -345,7 +345,7 @@ pub(crate) fn route_loop_pattern( )? { Some(domain_plan) => { // DomainPlan extracted successfully - trace::trace().pattern("route", "Pattern7_SplitScan (DomainPlan)", true); + trace::trace().pattern("route", "route=plan pattern=Pattern7_SplitScan (Phase 273)", true); // Step 1: Normalize DomainPlan → CorePlan let core_plan = PlanNormalizer::normalize(builder, domain_plan, ctx)?; @@ -376,7 +376,8 @@ pub(crate) fn route_loop_pattern( for entry in LOOP_PATTERNS { if (entry.detect)(builder, ctx) { // Phase 195: Use unified trace for pattern matching - trace::trace().pattern("route", entry.name, true); + let log_msg = format!("route=joinir pattern={} (Phase 194+)", entry.name); + trace::trace().pattern("route", &log_msg, true); return (entry.lower)(builder, ctx); } } @@ -387,7 +388,7 @@ pub(crate) fn route_loop_pattern( trace::trace().debug( "route", &format!( - "No pattern matched for function '{}' (pattern_kind={:?})", + "route=none (no pattern matched) func='{}' pattern_kind={:?} (exhausted: plan+joinir)", ctx.func_name, ctx.pattern_kind ), );