Files
hakorune/docs/development/current/main/phases/phase-273/README.md

139 lines
5.8 KiB
Markdown
Raw Normal View History

# Phase 273: Plan Extractor (Pure) + PlanLowerer SSOT
Status: ✅ P0/P1/P2 completed (2025-12-22)
Goal:
- pattern 列挙の裾広がりを止める。
- pattern は "検出して Plan を返すだけ" に降格し、CFG/PHI/block/value の生成責務を 1 箇所に閉じ込める。
- P1: DomainPlan → CorePlan の 2層構造で "収束" を強める
---
## P2 完了 (2025-12-22)
P2 では Pattern7split scanを Plan ラインへ移行し、P1 の CorePlan を保ったまま “収束圧” を上げた。
- ✅ Pattern7: Extractor → DomainPlan → Normalizer → CorePlan → LowererMIR/Frag/emit_fragへ統一
- ✅ CoreLoopPlan: `block_effects / phis / frag / final_values` で一般化Pattern6/7 が同一 CorePlan に収束)
- ✅ CoreEffectPlan: `dst: Option<ValueId>` + `effects: EffectMask` で副作用(例: `push`)を表現可能にした
- ✅ Lowerer: “split” の知識を持たず、CorePlan のみを処理pattern-agnostic 維持)
## P1 完了 (2025-12-22)
### アーキテクチャ
```
DomainPlan (Pattern固有)
↓ PlanNormalizer (SSOT)
CorePlan (固定語彙 - 構造ノードのみ)
↓ PlanVerifier (fail-fast)
↓ PlanLowerer
MIR (block/value/phi)
```
### SSOT Entry Point
**Files**:
- `src/mir/builder/control_flow/plan/mod.rs` - DomainPlan/CorePlan 型定義
- `src/mir/builder/control_flow/plan/normalizer.rs` - PlanNormalizerDomainPlan → CorePlan
- `src/mir/builder/control_flow/plan/verifier.rs` - PlanVerifierfail-fast 検証)
- `src/mir/builder/control_flow/plan/lowerer.rs` - PlanLowererCorePlan → MIR
### 原則
- Extractor は **pure**builder 触り厳禁、DomainPlan を返すのみ)
- Normalizer は **SSOT**pattern 固有知識はここに集約)
- CorePlan の式は **ValueId 参照のみ**String 禁止 → 第2の言語処理系を作らない
- Lowerer は **pattern-agnostic**CorePlan のみを処理)
- terminator SSOT: Frag → emit_frag()
---
## CorePlan 固定語彙
```rust
pub enum CorePlan {
Seq(Vec<CorePlan>),
Loop(CoreLoopPlan),
If(CoreIfPlan),
Effect(CoreEffectPlan),
Exit(CoreExitPlan),
}
pub enum CoreEffectPlan {
MethodCall { dst, object, method, args, effects },
BinOp { dst, lhs, op, rhs },
Compare { dst, lhs, op, rhs },
Const { dst, value },
}
```
**増殖禁止ルール**:
- ード種別variantの追加は禁止
- `EffectPlan::ScanInit` のような scan専用 variant は禁止
- データ(フィールド、パラメータ)の追加は許容
---
## P1 Implementation Summary
**Files changed** (7 total):
- Modified: `src/mir/builder/control_flow/plan/mod.rs` - DomainPlan/CorePlan 型定義 (~220 lines)
- New: `src/mir/builder/control_flow/plan/normalizer.rs` - PlanNormalizer (~290 lines)
- New: `src/mir/builder/control_flow/plan/verifier.rs` - PlanVerifier (~180 lines)
- Modified: `src/mir/builder/control_flow/plan/lowerer.rs` - CorePlan 対応 (~250 lines)
- Modified: `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs` - DomainPlan 返却
- Modified: `src/mir/builder/control_flow/joinir/patterns/router.rs` - Normalizer + Verifier 経由
**Regression test**:
- ✅ phase254_p0_index_of_vm.sh (fixed needle, forward scan)
- ✅ phase258_p0_index_of_string_vm.sh (dynamic needle)
---
## LLVM harness の落とし穴Phase 258 で露出)
Phase 258 の `index_of_string`dynamic needleで、VM では正しいのに LLVM で `Result: 0` になるケースが露出した。
原因は Phase 273 P1 の本線DomainPlan→CorePlan→emit_fragではなく、LLVM harness / AOT ランタイム側の “契約” だった。
### 1) `params` を使わないと引数が silently に潰れる
MIR JSON の `params`ValueId の引数順を使わず、heuristic で「未定義の use」を拾うと、
`box` フィールド等を見落とした場合に **v1 が arg0 に誤マップ**され、needle が haystack と同一扱いになる。
- Fix: `src/llvm_py/builders/function_lower.py``func_data["params"]` を SSOT として優先する
### 2) “raw integer vs handle” 衝突で `Result` が 0 になる
AOT ランタイムnyrt`ny_main()` の返り値が **raw i64****handle(i64)** かを区別できない。
正しい raw 返り値(例: `6`)が、たまたま生成済みの handle id と衝突すると、IntegerBox ではないため `Result: 0` になりうる。
- Fix: `crates/nyash_kernel/src/lib.rs` の exit_code 抽出で、handle が IntegerBox 以外なら raw i64 として扱う
## References
- JoinIR SSOT overview: `docs/development/current/main/joinir-architecture-overview.md`
- Frag SSOT: `docs/development/current/main/design/edgecfg-fragments.md`
- Phase 272Pattern6/7, Frag適用: `docs/development/current/main/phases/phase-272/README.md`
## Instructions
- P0 Claude Code: `docs/development/current/main/phases/phase-273/P0-CLAUDE.md`
- P1 Claude Code: `docs/development/current/main/phases/phase-273/P1-CLAUDE.md`
- P2 Completion: `docs/development/current/main/phases/phase-273/P2-COMPLETION.md`
## Next (P3 proposal)
P2 で追加した “legacy fallback” を残したままだと、Lowerer の中に `emit_scan_with_init_edgecfg()` 等の旧経路が残り続ける。
収束を完成させるには、次を P3 で行うのが良い:
- Pattern6 を generalized CoreLoopPlan`frag/block_effects/phis/final_values`)に移行
- `lower_loop_legacy()` を撤去し、generalized 経路を SSOT 化Fail-Fast
- CoreLoopPlan の `Option<...>` フィールドを必須化(構造で “揺れ” を消す)
## Future Work (P2+)
1. **Pattern7/8/9 DomainPlan 追加**: Split, BoolPredicate 等を DomainPlan に追加
2. **Normalizer 拡張**: 各 DomainPlan → CorePlan 変換
3. **全 Pattern の Plan 化**: Pattern1-5 を段階的に Plan 化