Files
hakorune/docs/development/current/main/design/edgecfg-fragments.md

617 lines
26 KiB
Markdown
Raw Normal View History

# EdgeCFG Flow FragmentsFrag / ExitKind— Structured→CFG lowering SSOT
docs(edgecfg): Phase 280 - Frag Composition SSOT Positioning (A→B→C) ## Purpose Stop pattern number enumeration proliferation by establishing Frag composition API as the Single Source of Truth (SSOT) for structured control flow → CFG lowering. Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts. The architectural SSOT is **Frag composition rules** (seq/if/loop/cleanup). ## Changes Summary **Phase A (Docs-only, no code)**: SSOT Positioning - edgecfg-fragments.md: Status Draft → Active SSOT (+243 lines) - Added 5 sections: Composition SSOT, Rules, Laws, Fail-Fast, Ownership - Documented 3-tier ownership model (Normalizer/Composition/Lowerer) - Established composition as pattern absorption destination - joinir-architecture-overview.md: Pattern absorption documentation (+90 lines) - Added Section 0.2: Pattern Number Absorption Destination - JoinIR vs Plan comparison (different extraction, same SSOT) - Pattern absorption status table (Pattern6/7 as Phase 280 targets) - phase-280/README.md: Full roadmap (new) **Phase B (API solidification)**: Contract Verification - compose.rs: Module-level + function-level Phase 280 docs (+149 lines) - Documented composition SSOT, ownership model, usage example - Added constraint/composition law sections to seq/if/loop/cleanup - Contract verification: All seq/if/loop contracts verified (no gaps) - Test gap analysis: No missing tests (wires/exits separation explicitly tested) **Phase C (Pattern preparation)**: Documentation-only - normalizer.rs: Pattern6/7 TODO comments (+10 lines) - Pattern6: Early exit doesn't fit compose::if_() → cleanup() target - Pattern7: 挙動不変保証難 → compose::if_() migration deferred to Phase 281 ## Impact - **Net +460 lines** (docs-heavy, minimal code) - **4 files modified**, 1 directory created - **SSOT established**: Frag composition is now THE absorption destination - **導線固定**: Clear migration path for Pattern6/7 (Phase 281+) - **No behavior change**: Documentation-only for Phase C (tests not run) ## Phase 280 Goal Achieved ✅ SSOT positioning + 導線固定 (NOT full migration - that's Phase 281) ✅ Phase A complete: Docs updated to "Active SSOT" ✅ Phase B complete: API contract verified and documented ✅ Phase C complete: Pattern6/7 hand-rolled locations documented ## Next Phase (Phase 281+) - Phase 281: Full Pattern6/7 absorption (replace hand-rolled with compose_*) - Phase 282: Router shrinkage (pattern numbers → test labels) - Phase 283+: Pattern8 and beyond 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 01:18:36 +09:00
Status: Active SSOT
Last updated: 2025-12-23
Phase: 280 (Composition SSOT Positioning)
Related:
- North starCFG/ABI: `docs/development/current/main/design/join-explicit-cfg-construction.md`
- Catch/Cleanup/Async: `docs/development/current/main/design/exception-cleanup-async.md`
## 目的(なぜ必要?)
EdgeCFGblock-parameterized CFG / edge-args SSOTが固まると、次に残る “泥沼” はここだけになる:
- **構造化制御if/loop + catch/cleanup→ CFG** の lowering で起きる **exit 配線問題**
- 「pattern番号で推測分岐」が増殖しやすい領域長期的には消したい
この文書は「pattern番号の列挙」を設計の中心にしないために、Structured→CFG の lowering を
**合成代数fragment composition**として SSOT 化する。
結論(本書の北極星):
- “分岐の中心” は pattern番号ではなく **ExitKind****Fragfragment** に置く
- 値の合流は EdgeCFG の **block params + edge-args** で表し、PHI/推測/メタに逃げない
- pattern は「Extractor形の認識/ Plan最小要件の抽出」までに縮退し、merge/配線層へ逆流させない
## “フロー” は 2 層ある(混ぜると崩れる)
1. **CFG層EdgeCFG / plumbing**
- terminator 語彙: `Jump/Branch/Return/Invoke`
- edge-args: terminator operand が SSOT
- out_edges の参照点が SSOT複数 edge 前提)
2. **Structured→CFG lowering 層flow composition**
- `if/loop/catch/cleanup/seq` を “Frag の合成” として書く
- 難しさの本体は **exit脱出の種類****ネスト****合流**
## コア概念(最小の強い箱)
### ExitKind脱出の種類を一次概念にする
最低限の ExitKind:
- `Normal`fallthrough
- `Break(loop_id)` / `Continue(loop_id)`
- `Return`
- `Unwind`Invoke.err / catch へ)
- `Cancel`async の drop/cancel 用。今は予約)
### EdgeStub未配線の脱出エッジ
“どこへ飛ぶべきか未確定” な edge を表す。最終的に EdgeCFG の terminator edge に落ちる。
例(概念):
- `from: BlockId`
- `kind: ExitKind`
- `args: EdgeArgs`(ターゲット params に対応する値。target が未確定でも “役割” はここで決める)
### Fragfragment
```text
Frag = { entry_block, exits: Map<ExitKind, Vec<EdgeStub>> }
```
- `entry_block`: 断片の入口
- `exits`: 断片から外へ出る未配線 edge の集合
## 合成則pattern列挙を写像へ落とす
### seq(a, b)
- `a.exits[Normal]``b.entry` へ接続するedge-args を必要に応じて写像)
- それ以外の exit は上位へ伝搬する
### if(cond, t, e)
- header に `Branch(cond, t.entry, e.entry)` を置く
- `t.Normal``e.Normal` は join へ集める(必要なら join block params を作る)
- `Break/Continue/Return/Unwind` は上位へ伝搬
### loop(body)
- header / latch / after を組み、`Continue` を header に戻す
- `Break` を after へ出す
- `Return/Unwind` は上位へ伝搬
### cleanup(body, cleanup_block)finally の後継)
狙い: “脱出 edge 正規化”
- body の全 exitNormal/Break/Continue/Return/Unwind/Cancelを cleanup 経由へリライトする
- cleanup 後に “元の exit” を再発射するExitTag + payload を block params で運ぶ)
重要: 例外 edgeInvoke.errも漏れなく cleanup に寄せる。
## pattern は最終的に消える?(設計としての答え)
消える(実装の中心概念から降格する)。
- pattern番号は **回帰テスト名/症状ラベル**としては残して良い
- 実装の中心は `Frag/ExitKind/join(block params)` の合成則になる
docs(edgecfg): Phase 280 - Frag Composition SSOT Positioning (A→B→C) ## Purpose Stop pattern number enumeration proliferation by establishing Frag composition API as the Single Source of Truth (SSOT) for structured control flow → CFG lowering. Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts. The architectural SSOT is **Frag composition rules** (seq/if/loop/cleanup). ## Changes Summary **Phase A (Docs-only, no code)**: SSOT Positioning - edgecfg-fragments.md: Status Draft → Active SSOT (+243 lines) - Added 5 sections: Composition SSOT, Rules, Laws, Fail-Fast, Ownership - Documented 3-tier ownership model (Normalizer/Composition/Lowerer) - Established composition as pattern absorption destination - joinir-architecture-overview.md: Pattern absorption documentation (+90 lines) - Added Section 0.2: Pattern Number Absorption Destination - JoinIR vs Plan comparison (different extraction, same SSOT) - Pattern absorption status table (Pattern6/7 as Phase 280 targets) - phase-280/README.md: Full roadmap (new) **Phase B (API solidification)**: Contract Verification - compose.rs: Module-level + function-level Phase 280 docs (+149 lines) - Documented composition SSOT, ownership model, usage example - Added constraint/composition law sections to seq/if/loop/cleanup - Contract verification: All seq/if/loop contracts verified (no gaps) - Test gap analysis: No missing tests (wires/exits separation explicitly tested) **Phase C (Pattern preparation)**: Documentation-only - normalizer.rs: Pattern6/7 TODO comments (+10 lines) - Pattern6: Early exit doesn't fit compose::if_() → cleanup() target - Pattern7: 挙動不変保証難 → compose::if_() migration deferred to Phase 281 ## Impact - **Net +460 lines** (docs-heavy, minimal code) - **4 files modified**, 1 directory created - **SSOT established**: Frag composition is now THE absorption destination - **導線固定**: Clear migration path for Pattern6/7 (Phase 281+) - **No behavior change**: Documentation-only for Phase C (tests not run) ## Phase 280 Goal Achieved ✅ SSOT positioning + 導線固定 (NOT full migration - that's Phase 281) ✅ Phase A complete: Docs updated to "Active SSOT" ✅ Phase B complete: API contract verified and documented ✅ Phase C complete: Pattern6/7 hand-rolled locations documented ## Next Phase (Phase 281+) - Phase 281: Full Pattern6/7 absorption (replace hand-rolled with compose_*) - Phase 282: Router shrinkage (pattern numbers → test labels) - Phase 283+: Pattern8 and beyond 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 01:18:36 +09:00
- 各 pattern 実装は "Extractor形の認識→ Frag 合成呼び出し" の薄い層へ縮退する
---
## Composition SSOT (Phase 280)
**Status**: Active SSOT
**Purpose**: Pattern number absorption destination
**Date**: 2025-12-23
### Why Composition is SSOT
Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts.
The architectural SSOT is **Frag composition rules** (`seq`/`if`/`loop`/`cleanup`).
**Upstream (Extractor/Normalizer)**: Finish "shape recognition" and extract pattern-specific knowledge
**Downstream (Composition)**: Use Frag composition rules to build CFG converging to SSOT
**Terminator Generation**: `emit_frag()` as sole SSOT (Phase 267 P0)
### Composition Input/Output Contract
- **Input**: `Frag` (entry + exits + wires + branches)
- **Output**: `Frag` (new entry + merged exits + merged wires + merged branches)
- **Guarantee**: Composition preserves invariants (`verify_frag_invariants_strict`)
- **No Allocation**: Caller (Normalizer) allocates `BasicBlockId`/`ValueId`
- **Pure Transform**: Composition rearranges `exits`/`wires`/`branches` only
### Composition Rules (Canonical Operations)
#### `seq(a, b)`: Sequential composition
**Composition Law**:
- `a.Normal` exits → `wires` (target = `Some(b.entry)`)
- Non-Normal exits (Return/Break/Continue/Unwind) → propagate upward (`exits`)
- Result: `seq.entry = a.entry`, `seq.exits = a.non-Normal + b.all`
**Contract**:
- **Caller allocates**: `b.entry` (`BasicBlockId`)
- **Composition wires**: `a.Normal``b.entry`
#### `if_(header, cond, t, e, join_frag)`: Conditional composition
**Composition Law**:
- `header``t.entry`/`e.entry` (`BranchStub``branches`)
- `t/e.Normal``join_frag.entry` (`EdgeStub``wires`)
- Non-Normal exits → propagate upward (`exits`)
- Result: `if.entry = header`, `if.exits = t/e.non-Normal + join_frag.all`
**Contract**:
- **Caller allocates**: `header`, `t.entry`, `e.entry`, `join_frag.entry` (`BasicBlockId`), `cond` (`ValueId`)
- **Caller provides**: `then_entry_args`, `else_entry_args` (`EdgeArgs`) - Phase 268 P1 SSOT
- **Composition wires**: `t/e.Normal``join_frag.entry`
#### `loop_(loop_id, header, after, body)`: Loop composition
**Composition Law**:
- `Continue(loop_id)``header` (`EdgeStub``wires`)
- `Break(loop_id)``after` (`EdgeStub``wires`)
- Normal/Return/Unwind → propagate upward (`exits`)
- Result: `loop.entry = header`, `loop.exits = Normal/Return/Unwind only` (no Break/Continue)
**Contract**:
- **Caller allocates**: `loop_id` (`LoopId`), `header`, `after` (`BasicBlockId`)
- **Composition wires**: `Continue(loop_id)``header`, `Break(loop_id)``after`
#### `cleanup(body, cleanup_block)`: Cleanup composition (TODO: Phase 280+)
**Planned Composition Law** (Future):
- All exits (Normal/Break/Continue/Return/Unwind) → `cleanup` (`EdgeStub``wires`)
- Cleanup re-dispatches original exit (`ExitTag` + payload via block params)
- `Invoke.err` also routed through cleanup
**Status**: Signature fixed, implementation TODO (Phase 280+)
---
## Composition Laws (Invariants)
### Wires/Exits Separation (Phase 265 P2)
**Invariants**:
- **`wires`**: `target = Some(...)` only (internal wiring, resolved)
- **`exits`**: `target = None` only (external exit, unresolved)
- **Exception**: `Return` may have `target = None` in `wires` (`emit_wires` ignores it)
**Why separate?**
- Prevents resolved wiring from being re-wired in next composition
- Makes composition semantics clear: `wires` = done, `exits` = propagate upward
**Fail-Fast Enforcement**:
- `verify_frag_invariants_strict()` checks `wires`/`exits` separation
- Normal/Break/Continue/Unwind require `target = Some` in `wires`
- `Return` allows `target = None` (meaningless for `Return`)
### Terminator Uniqueness (Phase 267 P0)
**Invariant**: 1 block = 1 terminator
**Enforcement** (`emit_frag`):
- From grouping: ensures 1 block = 1 terminator
- Same block cannot have both `wire` and `branch`
- Same block cannot have multiple `wires` (from-grouping detects violation)
### Entry Consistency
**Invariants**:
- `Frag.entry` must be a valid `BasicBlockId`
- Composition preserves entry validity
- Entry points to first block in composed CFG fragment
---
## Fail-Fast Invariants (Phase 266+)
### Pre-emission Verification (Two Levels)
#### `verify_frag_invariants()` (warning-only)
- **Purpose**: Legacy compatibility mode
- **Behavior**: Logs warnings but doesn't fail
- **Usage**: Used by existing code during migration
#### `verify_frag_invariants_strict()` (Err on violation)
- **Purpose**: New code enforcement
- **Behavior**: Returns `Err` on invariant violation
- **Usage**: Called by `emit_frag()` automatically
- **Enforces**: wires/exits separation, target constraints
**Invariants checked**:
1. **Wires/Exits Separation**:
- wires have `target = Some` (except Return)
- exits have `target = None`
2. **Target Validity**:
- Normal/Break/Continue/Unwind require `target = Some` in wires
- Return allows `target = None`
### Emission-time Verification (`emit_frag` SSOT)
**`emit_frag()` responsibilities** (Phase 267 P0):
1. Call `verify_frag_invariants_strict()` before emission
2. Detect `target = None` violations (except Return)
3. Enforce 1 block = 1 terminator (from-grouping)
4. Detect wire/branch conflicts (same block)
**Terminator emission**:
- `wires` → Jump/Return terminators (`emit_wires` internally)
- `branches` → Branch terminators (`set_branch_with_edge_args`)
- Phase 260 terminator API (SSOT): `set_jump_with_edge_args`, `set_branch_with_edge_args`
### Composition-side Invariants
**Assumptions**:
- Composition functions **assume** input `Frag` is valid
- Composition **preserves** validity (output passes `verify_frag_invariants_strict`)
- Caller (Normalizer) responsible for initial `Frag` validity
---
## Ownership (Who Allocates What)
### Allocator Responsibilities (3-tier)
#### Tier 1: Normalizer (Pattern-Specific)
**Responsibilities**:
- Allocates `BasicBlockId` (`builder.next_block_id()`)
- Allocates `ValueId` (`builder.next_value_id()`)
- Knows pattern semantics (scan, split, etc.)
- Constructs initial `Frag` with valid blocks/values
**Example** (Pattern6 ScanWithInit):
```rust
let header_bb = builder.next_block_id();
let body_bb = builder.next_block_id();
let step_bb = builder.next_block_id();
// ... allocate all blocks upfront
```
#### Tier 2: Composition API (Pattern-Agnostic)
**Responsibilities**:
- Receives pre-allocated `BasicBlockId`/`ValueId`
- Rearranges `exits`/`wires`/`branches`
- Pure CFG transformation (no allocation, no semantics)
**Example** (compose::seq):
```rust
pub fn seq(a: Frag, b: Frag) -> Frag {
// Assume a.entry, b.entry are pre-allocated
// Just rearrange exits/wires
}
```
**Why no allocation?**
- Separation of concerns: allocation (pattern-specific) vs wiring (generic)
- Composability: composition functions can be called in any order without ID conflicts
- Testability: composition can be tested with fixed IDs (deterministic)
#### Tier 3: Lowerer (MIR Emission)
**Responsibilities**:
- Calls `emit_frag()` to generate MIR terminators
- Uses Phase 260 terminator API (`set_jump_with_edge_args`, `set_branch_with_edge_args`)
- No allocation, no CFG construction
**Example** (PlanLowerer::lower_loop):
```rust
emit_frag(func, &loop_plan.frag)?; // Emits all terminators
```
### Ownership Flow Diagram
```
Pattern6/7 Normalizer
↓ (allocate blocks/values)
DomainPlan → CorePlan
↓ (pre-allocated IDs)
Composition API (seq/if/loop)
↓ (rearranged Frag)
PlanLowerer
↓ (emit_frag)
MIR Terminator Instructions
```
---
## 実装の入口SSOT API を先に作る)
目的: “どこを触ればいいか” を 1 箇所に固定し、推測・部分続行・場当たり分岐を減らす。
推奨の入口:
- `EdgeCFG` の plumbing API既存: `BasicBlock::out_edges()`
- Structured→CFG の入口 API新規: `Frag` / `ExitKind` / `compose::{seq, if_, loop_ , cleanup}`
物理配置(案):
- `src/mir/builder/control_flow/edgecfg/api/`(または `.../joinir/api/` に併設してもよい)
- `frag.rs` / `exit_kind.rs` / `compose.rs` / `patch.rs`
## verifyFail-Fast の置き場所)
- **NormalizeBox 直後**: terminator 語彙固定・edge-args 長さ一致・cond付きJump禁止など “意味SSOT” を確定
- **merge直前**: boundary/ABI/edge-args の矛盾を即死させ “配線SSOT” を確定
- **--verify**: PHI predecessor / CFG cache 整合 / edge-args の長さ一致を常設
## 直近の導入ステップ(最小で始める)
1. `Frag/ExitKind/EdgeStub` の型を追加docs+code 入口 SSOT
2. `seq/if/loop` の合成だけ実装cleanup/Invoke は後段)
3. 既存 pattern のうち 1 本だけ `Frag` 合成に寄せるPattern8 推奨)
4. 2 本目で再利用が見えたら "pattern番号での枝刈り" を削って合成側へ寄せる
## Loop に関する注意JoinIR-only
- `cf_loop` は JoinIR-onlyHard Freeze。EdgeCFG の “loop 直適用” を急いで別経路に生やすと SSOT が割れる。
- loop の EdgeCFG 化は、まず **BasicBlockId 層で持っている箇所Phase 268 の if_form のような場所)**から適用を進める。
- JoinIR 側の loop は Phase 270 で **fixture/smoke による SSOT 固定**を先に行い、壊れたら最小差分で直す。
補足Phase 270:
- Pattern1simple_while_minimalは test-only stub のため、一般ループの “基準” には使えない。
- Phase 270 では “最小の固定形” を Pattern9AccumConstLoopとして追加し、後で Frag 合成側へ吸収される前提で橋渡しにする。
## Bridge patterns撤去条件SSOT
ここで言う “bridge pattern” は、既存の JoinIR ルートを壊さずに **最小の固定形を先に通す**ための一時パターン。
(例: Phase 270 の `Pattern9_AccumConstLoop`
- 原則:
- bridge pattern は **汎用化しない**固定形SSOT + fixture/smoke で仕様を固定するだけ)。
- 将来は `Frag/ExitKind` 合成側へ **吸収して削除**する前提で追加する。
feat(llvm/phi): Phase 277 P1 - fail-fast validation for PHI strict mode ## Summary Implemented fail-fast validation for PHI ordering and value resolution in strict mode. ## Changes ### P1-1: Strict mode for "PHI after terminator" - File: `src/llvm_py/phi_wiring/wiring.py::ensure_phi` - Behavior: `NYASH_LLVM_PHI_STRICT=1` → RuntimeError if PHI created after terminator - Default: Warning only (no regression) ### P1-2: Strict mode for "fallback 0" - File: `src/llvm_py/phi_wiring/wiring.py::wire_incomings` - Behavior: Strict mode forbids silent fallback to 0 (2 locations) - Location 1: Unresolvable incoming value - Location 2: Type coercion failure - Error messages point to next debug file: `llvm_builder.py::_value_at_end_i64` ### P1-3: Connect verify_phi_ordering() to execution path - File: `src/llvm_py/builders/function_lower.py` - Behavior: Verify PHI ordering after all instructions emitted - Debug mode: Shows "✅ All N blocks have correct PHI ordering" - Strict mode: Raises RuntimeError with block list if violations found ## Testing ✅ Test 1: strict=OFF - passes without errors ✅ Test 2: strict=ON - passes without errors (no violations in test fixtures) ✅ Test 3: debug mode - verify_phi_ordering() connected and running ## Scope - LLVM harness (Python) changes only - No new environment variables (uses existing 3 from Phase 277 P2) - No JoinIR/Rust changes (root fix is Phase 279) - Default behavior unchanged (strict mode opt-in) ## Next Steps - Phase 278: Remove deprecated env var support - Phase 279: Root fix - unify "2本のコンパイラ" pipelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 14:48:37 +09:00
### Bridge contractテンプレ / SSOT
bridge pattern を追加する場合は、最低限この “撤去条件” を先に書く(書けないなら追加しない)。
- **固定する fixture/smokeSSOT**
- fixture最小と smokeintegrationを必ず紐づける
- 「何が通れば撤去できるか」を machine-checkable にする
- **置換先(吸収先)の SSOT がある**
- Pattern番号列挙の反対側に、必ず “吸収先” を書く(例: `Frag/ExitKind` 合成、もしくは emission 入口)
- 吸収先が未確定な場合でも “層” は確定させるpattern層にロジックを増やさない
- **撤去条件(最低限)**
1. 置換先(吸収先)で同じ fixture/smoke が PASS する
2. bridge pattern 依存の分岐が router から消せる(最小差分で削除できる)
3. quick/integration の FAIL 位置が悪化しない既知Failは増やさない
- **撤去手順(最小)**
- router から bridge pattern を外す
- fixture/smoke+ quickで PASS 維持
- ファイル削除(または historical へ隔離し、SSOT から参照を外す
### Phase 271: `Pattern9_AccumConstLoop` 撤去条件SSOT
Phase 270 の “JoinIR-only minimal loop” を通すための橋渡し。将来は Frag 合成側へ吸収して削除する。
- **固定 fixture/smoke**
- fixture: `apps/tests/phase270_p0_loop_min_const.hako`exit=3
- smoke: `tools/smokes/v2/profiles/integration/apps/phase270_p0_loop_min_const_vm.sh`
- **吸収先(層)**
- Structured→CFG lowering 層(`Frag/ExitKind` 合成)またはその emission 入口pattern層は extractor に縮退)
- **撤去条件**
1. 上記 fixture/smoke が、bridge pattern を使わない経路で PASS するFrag/emit_frag 側で loop を構築できる)
2. Pattern9 が router から削除されても coverage が落ちない(同 fixture が同じルートで通る)
3. `tools/smokes/v2/run.sh --profile quick` が悪化しない
- **撤去手順**
- Pattern9 の router 分岐を削除 → smoke PASS → Pattern9 実装を削除(または historical 化)
## 実装入口(コード SSOT
docs(edgecfg): Phase 280 - Frag Composition SSOT Positioning (A→B→C) ## Purpose Stop pattern number enumeration proliferation by establishing Frag composition API as the Single Source of Truth (SSOT) for structured control flow → CFG lowering. Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts. The architectural SSOT is **Frag composition rules** (seq/if/loop/cleanup). ## Changes Summary **Phase A (Docs-only, no code)**: SSOT Positioning - edgecfg-fragments.md: Status Draft → Active SSOT (+243 lines) - Added 5 sections: Composition SSOT, Rules, Laws, Fail-Fast, Ownership - Documented 3-tier ownership model (Normalizer/Composition/Lowerer) - Established composition as pattern absorption destination - joinir-architecture-overview.md: Pattern absorption documentation (+90 lines) - Added Section 0.2: Pattern Number Absorption Destination - JoinIR vs Plan comparison (different extraction, same SSOT) - Pattern absorption status table (Pattern6/7 as Phase 280 targets) - phase-280/README.md: Full roadmap (new) **Phase B (API solidification)**: Contract Verification - compose.rs: Module-level + function-level Phase 280 docs (+149 lines) - Documented composition SSOT, ownership model, usage example - Added constraint/composition law sections to seq/if/loop/cleanup - Contract verification: All seq/if/loop contracts verified (no gaps) - Test gap analysis: No missing tests (wires/exits separation explicitly tested) **Phase C (Pattern preparation)**: Documentation-only - normalizer.rs: Pattern6/7 TODO comments (+10 lines) - Pattern6: Early exit doesn't fit compose::if_() → cleanup() target - Pattern7: 挙動不変保証難 → compose::if_() migration deferred to Phase 281 ## Impact - **Net +460 lines** (docs-heavy, minimal code) - **4 files modified**, 1 directory created - **SSOT established**: Frag composition is now THE absorption destination - **導線固定**: Clear migration path for Pattern6/7 (Phase 281+) - **No behavior change**: Documentation-only for Phase C (tests not run) ## Phase 280 Goal Achieved ✅ SSOT positioning + 導線固定 (NOT full migration - that's Phase 281) ✅ Phase A complete: Docs updated to "Active SSOT" ✅ Phase B complete: API contract verified and documented ✅ Phase C complete: Pattern6/7 hand-rolled locations documented ## Next Phase (Phase 281+) - Phase 281: Full Pattern6/7 absorption (replace hand-rolled with compose_*) - Phase 282: Router shrinkage (pattern numbers → test labels) - Phase 283+: Pattern8 and beyond 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 01:18:36 +09:00
**Phase 280: Composition SSOT Positioning Complete (2025-12-23)**
- **Status**: Active SSOT
- **Documentation**: Full composition SSOT positioning (5 sections above)
- **Implementation**: Composition API exists and tested (seq/if/loop)
- **Pattern Preparation**: Pattern6/7 hand-rolled locations documented for Phase 281 migration
**Phase 264 (歴史/別案件)**: Entry API Creation (別スコープ: BundleResolver loop fix)
docs(edgecfg): Phase 280 - Frag Composition SSOT Positioning (A→B→C) ## Purpose Stop pattern number enumeration proliferation by establishing Frag composition API as the Single Source of Truth (SSOT) for structured control flow → CFG lowering. Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts. The architectural SSOT is **Frag composition rules** (seq/if/loop/cleanup). ## Changes Summary **Phase A (Docs-only, no code)**: SSOT Positioning - edgecfg-fragments.md: Status Draft → Active SSOT (+243 lines) - Added 5 sections: Composition SSOT, Rules, Laws, Fail-Fast, Ownership - Documented 3-tier ownership model (Normalizer/Composition/Lowerer) - Established composition as pattern absorption destination - joinir-architecture-overview.md: Pattern absorption documentation (+90 lines) - Added Section 0.2: Pattern Number Absorption Destination - JoinIR vs Plan comparison (different extraction, same SSOT) - Pattern absorption status table (Pattern6/7 as Phase 280 targets) - phase-280/README.md: Full roadmap (new) **Phase B (API solidification)**: Contract Verification - compose.rs: Module-level + function-level Phase 280 docs (+149 lines) - Documented composition SSOT, ownership model, usage example - Added constraint/composition law sections to seq/if/loop/cleanup - Contract verification: All seq/if/loop contracts verified (no gaps) - Test gap analysis: No missing tests (wires/exits separation explicitly tested) **Phase C (Pattern preparation)**: Documentation-only - normalizer.rs: Pattern6/7 TODO comments (+10 lines) - Pattern6: Early exit doesn't fit compose::if_() → cleanup() target - Pattern7: 挙動不変保証難 → compose::if_() migration deferred to Phase 281 ## Impact - **Net +460 lines** (docs-heavy, minimal code) - **4 files modified**, 1 directory created - **SSOT established**: Frag composition is now THE absorption destination - **導線固定**: Clear migration path for Pattern6/7 (Phase 281+) - **No behavior change**: Documentation-only for Phase C (tests not run) ## Phase 280 Goal Achieved ✅ SSOT positioning + 導線固定 (NOT full migration - that's Phase 281) ✅ Phase A complete: Docs updated to "Active SSOT" ✅ Phase B complete: API contract verified and documented ✅ Phase C complete: Pattern6/7 hand-rolled locations documented ## Next Phase (Phase 281+) - Phase 281: Full Pattern6/7 absorption (replace hand-rolled with compose_*) - Phase 282: Router shrinkage (pattern numbers → test labels) - Phase 283+: Pattern8 and beyond 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-23 01:18:36 +09:00
- Note: Phase 264 は別案件BundleResolver loop pattern fix
- Composition API 入口作成は Phase 264 で完了したが、SSOT positioning は Phase 280 で確立
- 物理配置: `src/mir/builder/control_flow/edgecfg/api/`
- コア型: `ExitKind`, `EdgeStub`, `Frag`
- 合成関数: `seq`, `if_`, `loop_`, `cleanup`シグネチャのみ、中身TODO
- 検証: `verify_frag_invariants`(空実装)
**Phase 265 P0 で最小実装完了**
- `compose::loop_()`: exit集合の分類実装配線なし、P1以降
- `verify_frag_invariants()`: 最小検証追加(デバッグガード付き)
- Pattern8適用: P0ではやらない偽Frag回避、P1から実戦投入
**Phase 265 P1: 配線ロジック実装完了**
**目的**: Frag/ExitKind が BasicBlockId 層で配線できることを証明
**実装完了内容**:
- EdgeStub に `target: Option<BasicBlockId>` 追加
- compose::loop_() が Continue → header, Break → after への配線を実行
- verify_frag_invariants() が配線契約を検証(デバッグモード)
- test-only PoC で配線の実証完了5個のテスト
**配線契約**:
- Continue(loop_id) の EdgeStub.target = Some(header)
- Break(loop_id) の EdgeStub.target = Some(after)
- Normal/Return/Unwind の EdgeStub.target = None上位へ伝搬
**Phase 265 P1 のスコープ**:
- ✅ Frag 層での配線ロジック
- ✅ BasicBlockId 層でのテスト証明
- ❌ MIR 命令生成Phase 266+
- ❌ NormalizedShadow/JoinIR層への適用Phase 266+、JoinIR-VM Bridge 改修後)
feat(edgecfg): Phase 265 P2 - seq/if_ 実装(wires/exits 分離) ## 目的 「解決済み配線(wires)」と「未解決 exit(exits)」を分離し、 Frag 合成の基本パターンを完成させる。 ## 実装内容 ### 1. Frag 構造体の変更 - `wires: Vec<EdgeStub>` フィールド追加 - 不変条件: - exits: target = None のみ(未配線、外へ出る exit) - wires: target = Some(...) のみ(配線済み、内部配線) ### 2. loop_() の wires 対応 - Break/Continue を exits から wires に移動 - P1 テスト 3個を wires 検証に更新 ### 3. seq(a, b) 実装 - a.Normal → b.entry を wires に追加(内部配線) - seq の exits[Normal] は b の Normal のみ - 新規テスト 2個追加 ### 4. if_(header, cond, t, e, join_frag) 実装 - シグネチャ変更: join: BasicBlockId → join_frag: Frag - t/e.Normal → join_frag.entry を wires に追加 - if の exits は join_frag.exits - 新規テスト 2個追加 ### 5. verify_frag_invariants() 強化 - wires/exits 分離契約の検証追加(警告のみ) - Err 化は Phase 266 で実施 ## テスト結果 - edgecfg::api: 13/13 PASS(frag 3 + compose 9 + verify 1) - 全 lib テスト: 1388/1388 PASS(退行なし) ## 核心的な設計判断 1. **wires/exits 分離**: - 問題: 解決済み配線と未解決 exit を混ぜると再配線バグ - 解決: 分離して不変条件を強化 - 効果: Phase 266 で wires を MIR terminator に落とすだけ 2. **if_ は join_frag 受け取り**: - 問題: join: BasicBlockId では「join block」か「join 以降」か曖昧 - 解決: join_frag: Frag で「join 以降」を明確化 - 効果: PHI 生成の柔軟性確保 3. **verify は警告のみ**: - P2 の役割: wires/exits 分離の証明に集中 - Phase 266 で MIR 生成時に厳格化 ## 次フェーズへの橋渡し - Phase 266: wires を MIR terminator に落とす - Phase 267: Pattern6/7/8 を Frag 化 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 16:47:47 +09:00
**Phase 265 P2 完了2025-12-21**
**実装完了内容**:
- ✅ Frag に `wires: Vec<EdgeStub>` フィールド追加
- ✅ wires/exits 分離設計確立
- **exits**: target = None のみ(未配線、外へ出る exit
- **wires**: target = Some(...) のみ(配線済み、内部配線)
- ✅ loop_() を wires 対応に更新Break/Continue → wires
- ✅ seq(a, b) 実装完了a.Normal → wires
- ✅ if_(header, cond, t, e, join_frag) 実装完了t/e.Normal → wires
- ✅ verify_frag_invariants() に wires/exits 分離契約追加(警告のみ)
- ✅ 全テスト PASS13個: frag 3個 + compose 9個 + verify 1個
**設計判断の記録**:
1. **なぜ wires/exits を分離するか?**
- 問題: 解決済み配線と未解決 exit を混ぜると、次の合成で内部配線が再度配線対象になる
- 決定: wires/exits を分離し、不変条件を強化
- 理由: 合成の意味が素直になり、Phase 266 で wires を MIR terminator に落とすだけ
2. **なぜ if_ は join_frag を受け取るか?**
- 問題: join: BasicBlockId だと、if の Normal exit が「join block」か「join 以降」か曖昧
- 決定: join_frag: Frag を受け取る
- 理由: if の Normal exit = join 以降join_frag.exitsが明確、PHI 生成の柔軟性確保
3. **なぜ verify は警告のみか?**
- P2 の役割: wires/exits 分離の証明に集中MIR 命令生成なし)
- Phase 266 で MIR 生成時に verify を厳格化target 違反 → Err
**次フェーズへの橋渡し**:
次フェーズPhase 266: wires → MIR terminator 生成test-only PoC
- wires を MIR terminator に落とす SSOT を追加(`emit_wires`
- verify の strict 版を追加(`verify_frag_invariants_strict`、段階導入)
次フェーズPhase 267: JoinIR/NormalizedShadow への適用 + Branch 生成
- NormalizedShadow/JoinIR で Frag/wires を実戦投入(層境界を守って段階的に)
- Branch の terminator 生成wires → MIRを追加
feat(edgecfg): Phase 265 P2 - seq/if_ 実装(wires/exits 分離) ## 目的 「解決済み配線(wires)」と「未解決 exit(exits)」を分離し、 Frag 合成の基本パターンを完成させる。 ## 実装内容 ### 1. Frag 構造体の変更 - `wires: Vec<EdgeStub>` フィールド追加 - 不変条件: - exits: target = None のみ(未配線、外へ出る exit) - wires: target = Some(...) のみ(配線済み、内部配線) ### 2. loop_() の wires 対応 - Break/Continue を exits から wires に移動 - P1 テスト 3個を wires 検証に更新 ### 3. seq(a, b) 実装 - a.Normal → b.entry を wires に追加(内部配線) - seq の exits[Normal] は b の Normal のみ - 新規テスト 2個追加 ### 4. if_(header, cond, t, e, join_frag) 実装 - シグネチャ変更: join: BasicBlockId → join_frag: Frag - t/e.Normal → join_frag.entry を wires に追加 - if の exits は join_frag.exits - 新規テスト 2個追加 ### 5. verify_frag_invariants() 強化 - wires/exits 分離契約の検証追加(警告のみ) - Err 化は Phase 266 で実施 ## テスト結果 - edgecfg::api: 13/13 PASS(frag 3 + compose 9 + verify 1) - 全 lib テスト: 1388/1388 PASS(退行なし) ## 核心的な設計判断 1. **wires/exits 分離**: - 問題: 解決済み配線と未解決 exit を混ぜると再配線バグ - 解決: 分離して不変条件を強化 - 効果: Phase 266 で wires を MIR terminator に落とすだけ 2. **if_ は join_frag 受け取り**: - 問題: join: BasicBlockId では「join block」か「join 以降」か曖昧 - 解決: join_frag: Frag で「join 以降」を明確化 - 効果: PHI 生成の柔軟性確保 3. **verify は警告のみ**: - P2 の役割: wires/exits 分離の証明に集中 - Phase 266 で MIR 生成時に厳格化 ## 次フェーズへの橋渡し - Phase 266: wires を MIR terminator に落とす - Phase 267: Pattern6/7/8 を Frag 化 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 16:47:47 +09:00
Phase 267: Pattern6/7/8 への展開
- Pattern6 (ScanWithInit) を Frag 化
- Pattern7 (SplitScan) を Frag 化
- Pattern8 (BoolPredicateScan) を Frag 化
- 再利用性の確認pattern番号分岐削減
現時点では既存 pattern6/7/8 や merge/EdgeCFG は未改変(合成能力の証明のみ)。
**Phase 266 P0-P2 完了2025-12-21**
**実装完了内容**:
- ✅ emit.rs 作成wires → MIR terminator 変換の SSOT
- emit_wires() 実装from グループ化 + Return の target=None 許可)
- unit test 4個jump/return/unwired/multiple_from_same_block
- ✅ verify_frag_invariants_strict() 追加(段階導入を壊さない)
- 既存の verify_frag_invariants() は変更なし(警告のまま)
- wires/exits 分離契約を Err 化Return の target=None は許可)
- ✅ mod.rs 更新emit module エクスポート)
- ✅ 全テスト PASS1392 passed: 既存 1388 + 新規 4個
**実装の核心原則**:
1. **from ごとにグループ化して1本だけ許可**
- BTreeMap で from ごとにグループ化
- 1 block = 1 terminator 制約を厳格に強制
2. **Return は target=None を許可**
- Return は target が意味を持たないemit_wires で無視される)
- Fail-Fast 対象は Normal/Break/Continue/Unwind の target=None のみ
3. **verify_frag_invariants_strict() 別名で用意**
- 既存の verify_frag_invariants() は警告のまま維持
- 新規 verify_frag_invariants_strict() で Err 化
- PoC/emit 側だけ strict を使用(段階導入を壊さない)
4. **Phase 260 terminator 語彙ルールを厳守**
- Jump: set_jump_with_edge_args() を使用
- Return: set_terminator() + set_return_env() を使用
**設計判断の記録**:
1. **なぜ from グループ化が必要か?**
- 問題: 同じ block に複数 terminator を設定すると上書きになる
- 決定: from ごとにグループ化し、1本だけ許可Fail-Fast
- 理由: 1 block = 1 terminator は MIR の不変条件
2. **なぜ Return は target=None を許可するか?**
- 問題: Return は呼び出し元に戻るので、target が意味を持たない
- 決定: Return のみ target=None を許可
- 理由: Normal/Break/Continue/Unwind は明確な target が必要
3. **なぜ verify_frag_invariants_strict() を別名にしたか?**
- 問題: 既存の verify_frag_invariants() を Err 化すると、既存コードが壊れる
- 決定: 新規に strict 版を追加し、段階導入
- 理由: Phase 267+ で既存コードを段階的に strict へ移行
**次フェーズへの橋渡し**:
## Phase 267P0完了: Branch の第一級化BranchStub + emit_frag
- 目的: Frag に Branch を第一級で追加し、wiresJump/Returnと同様に MIR terminator へ落とす入口を作る。
- 追加:
- `BranchStub`header→then/else の分岐を表現)
- `Frag.branches: Vec<BranchStub>`Branch 専用、wires と分離)
- `emit_frag(function, frag)`SSOT: `emit_wires` + `set_branch_with_edge_args`、1 block=1 terminator を Fail-Fast
- スコープ:
- ✅ BasicBlockId 層で unit test により PoC 証明
- ❌ NormalizedShadow/JoinIR への実適用は Phase 268 に繰り越し(層境界維持)
詳細: `docs/development/current/main/phases/phase-267/README.md`
## Phase 268完了: if_form.rs への Frag 適用 + compose::if_ Entry Edge-args SSOT化
### P0: 最小適用emission 層経由)
- 目的: EdgeCFG Fragment を "層を跨がずに" 実戦投入する
- 戦略: `if_form.rs` に直接 Frag 構築コードを書かず、`emission/branch.rs` に薄い入口関数 `emit_conditional_edgecfg()` を追加
- 理由:
1. **層が綺麗**: Frag 構築ロジックを emission 層に閉じ込める
2. **差分が小さい**: if_form.rs は API 呼び出し差し替えのみ3箇所削除 + 1箇所追加
3. **デバッグ容易**: 層境界が明確で問題切り分けが簡単
- 実装:
- emission/branch.rs に `emit_conditional_edgecfg()` 追加
- if_form.rs の `emit_conditional()` + `emit_jump()` 2箇所を削除し、新規 API 呼び出しに置換
- テスト結果:
- ✅ cargo build --release: 成功
- ✅ cargo test --lib --release: 1444/1444 PASS
- ✅ quick smoke: 45/46 PASS
### P1: compose::if_() Entry Edge-args SSOT化
- 目的: compose::if_() の then/else entry edge-args を呼び出し側 SSOT にし、TODO 削除Phase 267 P2+ からの継続)
- **核心原則**: compose::if_() 内部で then/else entry edge-args を "勝手に空 Vec で生成" しない → 呼び出し側が明示的に渡す
- 実装:
- compose::if_() シグネチャ変更: `if_(header, cond, t, then_entry_args, e, else_entry_args, join_frag)`
- emission/branch.rs::emit_conditional_edgecfg() から空 EdgeArgs を then/else 両方に渡す
- EdgeCFG テスト更新compose.rs 2箇所、emit.rs 1箇所
- TODO コメント削除完了
- テスト結果:
- ✅ cargo build --release: 成功
- ✅ cargo test --lib --release: 1444/1444 PASS
- ✅ quick smoke: 45/46 PASS
### アーキテクチャ
```
if_form.rs (MirBuilder 層)
↓ 呼び出し
emission/branch.rs::emit_conditional_edgecfg() (emission 層: 薄ラッパー)
↓ 内部で使用
Frag 構築 + compose::if_() + emit_frag() (EdgeCFG Fragment API)
↓ 最終的に呼び出し
set_branch_with_edge_args() / set_jump_with_edge_args() (Phase 260 SSOT)
```
詳細: `docs/development/current/main/phases/phase-268/README.md`
Phase 267: JoinIR Pattern への適用
- NormalizedShadow への Frag 適用
- Pattern6/7/8 を Frag 化
- Branch 生成 + pattern番号分岐削減
- fixture + smoke test