feat(mir/llvm): Phase 273 P0-P1 DomainPlan→CorePlan + LLVM arg fix

Phase 273 P0-P1: Two-layer plan architecture
- DomainPlan: Pattern-specific knowledge (ScanWithInit)
- CorePlan: Fixed vocabulary (Seq, Loop, If, Effect, Exit)
- ValueId references only (String expressions forbidden)
- Pipeline: Extractor→Normalizer→Verifier→Lowerer

New plan/ module:
- mod.rs: Type definitions, SSOT spec
- normalizer.rs: DomainPlan→CorePlan + ID allocation
- verifier.rs: V1-V6 invariant checks (fail-fast)
- lowerer.rs: CorePlan→MIR (pattern-agnostic)

LLVM fix (ChatGPT):
- function_lower.py: Fix argument reference bug
- Phase 258 index_of_string now PASS on LLVM backend

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-22 22:42:56 +09:00
parent f07c2e7874
commit 960241795d
20 changed files with 1728 additions and 388 deletions

View File

@ -0,0 +1,97 @@
# Phase 273 P0: Plan Extractorpure導入の最小収束Claude Code 指示書)
Status: instructions / design-first
目的:
- pattern を “分岐ロジック” ではなく “Plan抽出プラグイン” に降格する。
- block/value/phi の生成責務を **PlanLowerer に集約**し、裾広がりを止める。
制約:
- Extractor は **builder を触らない**`next_block_id/next_value_id/insert_phi/...` 禁止)
- 新しい env var 禁止
- by-name hardcode 禁止Box名/Pattern名の文字列分岐を増やさない
- terminator SSOT は維持(`Frag → emit_frag()`
参照:
- SSOT設計: `docs/development/current/main/design/edgecfg-fragments.md`
- 参照実装(既存): Phase 272/269 の Frag 経路
- `docs/development/current/main/phases/phase-272/README.md`
- `docs/development/current/main/phases/phase-269/README.md`
---
## Step 1: Plan の固定語彙を作る(増殖しない型)
新規モジュール(例):
- `src/mir/builder/control_flow/plan/`(または `src/mir/control_plan/`
最低限の語彙(案):
- `Plan::Seq(Vec<Plan>)`
- `Plan::Loop { params, body }`
- `Plan::If { cond, then_, else_ }`
- `Plan::Effect { ... }`(副作用の順序だけ表す)
- `Plan::Exit { kind, values }`
重要:
- Plan は AST/expr を “参照” するだけ(必要なら node id / span を持つ)
- Plan は “CFGを作らない”
---
## Step 2: Extractor を pure にするpattern の責務縮退)
対象:
- `src/mir/builder/control_flow/joinir/patterns/*`
方針:
- 既存の `extract_*_parts(...)` のような抽出関数を “Plan を返す” 形に寄せる
- `Ok(None)` / `Ok(Some(plan))` / `Err(...)` を明確に使い分ける
Fail-fast:
- “一致した” のに必要条件が満たせない場合は `Err`close-but-unsupported を黙って通さない)
---
## Step 3: PlanLowerer を作る(唯一の builder 触り役)
新規モジュール(例):
- `src/mir/builder/control_flow/plan/lowerer.rs`
責務:
- block/value/phi を作るのはここだけ
- Frag を組み立てて `emit_frag()` に渡す
- 既存の emissionPhase 272 の `loop_scan_with_init` / `loop_split_scan` など)を呼ぶのは Lowerer 側に寄せる
---
## Step 4: まず1つのパターンで PoC最小差分
推奨:
- Pattern6 または Pattern8 を 1つ選び、以下の順で移行:
1) extractor は plan を返すだけにする
2) lowerer が既存 emission を呼ぶ
3) router は “plan を得たら lowerer へ” にする
Acceptance:
- 既存の fixture/smoke が変わらず PASS既定挙動不変
- Extractor が builder を触っていないことgrep で確認)
---
## Step 5: docsSSOT更新
更新対象:
- `docs/development/current/main/phases/phase-273/README.md`
最低限:
- “Extractorはpure / Lowererだけが作る” を SSOT として明文化
- Plan語彙が固定であること増殖しない理由を短く書く
---
## 完了条件P0
- Plan の語彙が導入され、Extractor が pure になっている最低1パターンでPoC
- Lowerer が block/value/phi 生成の唯一の場所になっている
- terminator SSOTemit_fragが維持されている
- 既存スモークに退行がない

View File

@ -0,0 +1,34 @@
# Phase 273 P0: Plan Extractor (pure) + PlanLowerer SSOT — completion
Status: ✅ completed (2025-12-22)
Goal:
- Pattern を “Plan抽出プラグイン” に降格し、CFG/PHI/block/value 生成の責務を PlanLowerer に集約して、裾広がりを止める。
What changed (P0):
- Plan 語彙を最小導入P0 は PoC として pattern-specific を 1 個だけ許容)
- Extractor は purebuilder を触らない)
- Lowerer が block/value/phi を作る唯一の場所
- terminator SSOT`Frag → emit_frag()`)は維持
PoC target:
- Pattern6scan with init
Behavior / limits:
- `dynamic_needle=true` は当時 `Ok(None)` で fallthroughP1+
- `step_lit != 1`reverse scan`Ok(None)` で fallthroughP1+
Regression:
- `tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh` PASS
Files:
- `src/mir/builder/control_flow/plan/mod.rs`
- `src/mir/builder/control_flow/plan/lowerer.rs`
- `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
- `src/mir/builder/control_flow/joinir/patterns/router.rs`
Next (P1+):
- Plan語彙を固定`Seq/Loop/If/Effect/Exit`へ畳み、pattern-specific Plan の増殖を止める。
Note (follow-up):
- その後の修正で `dynamic_needle`Phase 258 の string index_of 系)も PlanLowerer 側で handling され、JoinIR freeze 下でも fallthrough しない経路に寄せている(詳細は Phase 273 README を参照)。

View File

@ -0,0 +1,117 @@
# Phase 273 P1: Plan語彙固定 + PlanVerifierfail-fastClaude Code 指示書)
Status: instructions / design-first
目的:
- P0 の pattern-specific Plan`Plan::ScanWithInit`)を、増殖しない固定語彙へ畳む。
- Extractor は pure を維持しつつ、Plan の整合性は Verifier で fail-fast に固定する。
スコープ:
- Plan 型の再設計(固定語彙への移行)
- PlanNormalizerDomainPlan → CorePlanの追加SSOT
- PlanVerifier の追加(境界で fail-fast
- Pattern6 を “固定語彙 CorePlan” で表現し直すPoC 維持)
Non-goals:
- 全 pattern の移行P2+
- 新しい言語機能
- env var 追加
参照:
- Phase 273 README: `docs/development/current/main/phases/phase-273/README.md`
- Phase 273 P0 completion: `docs/development/current/main/phases/phase-273/P0-COMPLETION.md`
- Frag SSOT: `docs/development/current/main/design/edgecfg-fragments.md`
---
## Step 1: Plan の固定語彙を導入する
方針:
- `Plan` の variant を “構造語彙” に固定する(増殖しない)。
- pattern-specific variant は廃止方向P1では Pattern6 から移行)。
最低限P1:
- `Plan::Seq(Vec<Plan>)`
- `Plan::Loop(LoopPlan)`
- `Plan::If(IfPlan)`
- `Plan::Effect(EffectPlan)`(副作用の順序だけ)
- `Plan::Exit(ExitPlan)`Return/Break/Continue…
注意:
- P1では “式の意味” を Plan に入れすぎないexpr は参照/ID/ASTNode を持つだけにする)。
- **式を `String` にしない**Lowerer 側に「文字列式の解釈器」を生やさない)。
---
## Step 1.5: DomainPlan → CorePlan の 2層 + PlanNormalizerSSOT
狙い:
- “固定語彙にしたいが、scan固有データも欲しい” の葛藤を構造で解消する。
- Lowerer を **CorePlan-only** にして、pattern固有知識の混入を防ぐ。
方針:
- `DomainPlan`: pattern 固有の最小表現(短命・増殖は許容するが「削る側」)
- `CorePlan`: 固定語彙のみ(増殖禁止)
- `PlanNormalizer`**唯一** `DomainPlan → CorePlan` を行うSSOT
重要:
- `EffectPlan::ScanInit` のような **scan専用 variant** は禁止
- 「あとで MethodCall に統一する」前提の例外は作らない(裾広がりの入口になる)
---
## Step 2: PlanVerifier を追加するfail-fast
新規モジュール案:
- `src/mir/builder/control_flow/plan/verifier.rs`
役割:
- Extractor が返した Plan が “Lowerer が扱える契約” を満たすかを検証する
- close-but-unsupported を `Err` にして誤コンパイル余地を潰す
P1で入れる最小の不変条件:
- Loop の header params / carriers が揃っている
- Exit が Loop 外へ飛ぶ場合の kind が妥当
- Effect が順序上の制約を破っていない最低限Effect がブロック境界で落ちない)
---
## Step 3: Pattern6 を固定語彙 Plan で表現し直す
方針:
- Extractor は `DomainPlan` を返すpure
- `PlanNormalizer``DomainPlan → CorePlan` を行い、固定語彙 `CorePlan::{Seq,Loop,If,Exit,Effect}` の組み合わせに落とす。
- Lowerer は `CorePlan` のみを処理するpattern固有知識を持たない
禁止P1:
- scan固有情報を `EffectPlan` の payload/variant として持つこと(固定語彙の侵食)
- 式を `String` として Plan に埋め込むこと(第二の処理系が生える)
---
## Step 4: Router の責務を整理する
方針:
- router は `extract_*` を呼び、`PlanVerifier` を通し、`PlanLowerer` に渡すだけ。
- router が builder を触らない(制御の入口は薄く保つ)。
推奨の順序P1:
1. `extract_*`pure, DomainPlan`Ok(None)` / `Ok(Some(domain))` / `Err`
2. `PlanNormalizer::normalize(domain) -> CorePlan`SSOT
3. `PlanVerifier::verify(&core)`fail-fast
4. `PlanLowerer::lower(core)`CorePlan-only
---
## Step 5: 回帰確認P0 と同等)
最低限:
- `tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh` PASS
---
## 完了条件P1
- Plan enum の variant が固定語彙に収束している(増殖しない)
- PlanVerifier が境界で fail-fast を提供している
- Pattern6 が固定語彙 Plan で表現され、P0のPoCが維持されている

View File

@ -0,0 +1,119 @@
# Phase 273: Plan Extractor (Pure) + PlanLowerer SSOT
Status: ✅ P0/P1 completed (2025-12-22)
Goal:
- pattern 列挙の裾広がりを止める。
- pattern は "検出して Plan を返すだけ" に降格し、CFG/PHI/block/value の生成責務を 1 箇所に閉じ込める。
- P1: DomainPlan → CorePlan の 2層構造で "収束" を強める
---
## 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 },
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`
## Future Work (P2+)
1. **Pattern7/8/9 DomainPlan 追加**: Split, BoolPredicate 等を DomainPlan に追加
2. **Normalizer 拡張**: 各 DomainPlan → CorePlan 変換
3. **全 Pattern の Plan 化**: Pattern1-5 を段階的に Plan 化