diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index cbaa1f2c..18cf0f89 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -12,7 +12,7 @@ - **25.x**: Stage0/Stage1/Stage‑B / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備。 - **25.1 系**: Stage‑B / Stage‑1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ライン。 - **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成(LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBox)と MirScanExitLiveness の準備。 - - **26-H(New)**: JoinIR 設計+ミニ実験フェーズ(制御構造を関数呼び出しに正規化する IR を小さく試す)。 + - **26-H / 27.x(New)**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備中(27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定済み。次は 27.6 で ExitPhiBuilder 縮退に着手予定)。 - Rust 側: - LoopForm v2 + ControlForm + Conservative PHI は、代表テスト(Stage‑1 UsingResolver / Stage‑B 最小ループ)ではほぼ安定。 - 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。 @@ -180,7 +180,74 @@ - チームレビュー - **長期**: NamingBox/UnifiedCallEmitter/VM の 3 点で「名前と arity の SSOT」完全統一。 -### 1-02. Phase 26-F — Loop Exit Liveness / BodyLocal PHI Guard(箱とガードの整備、2025-11-22 時点) +### 1-02. Phase 27.4-A — JoinIR Header φ 統合(LoopHeaderShape 導入、2025-11-23 完了) + +**目的** +- HeaderPhiBuilder が担っていた「loop header φ(Pinned/Carrier の合流)」の意味を、JoinIR 側に構造として持ち上げる足場を作る。 +- Rust 側の header φ 挙動は一切変えず、本線 MIR/LoopForm→VM を壊さないまま JoinIR 経路の設計を進める。 + +**やったこと** +- `src/mir/join_ir.rs` に `LoopHeaderShape { pinned, carriers }` を追加し、skip_ws / FuncScanner.trim のループについて: + - skip_ws: Pinned = [s, n], Carrier = [i](`loop_step(s, i, n)` の設計をコメント付きで固定)。 + - trim : Pinned = [str, b], Carrier = [e](`loop_step(str, b, e)` の設計をコメント付きで固定)。 +- Header φ の意味を「loop_step 引数としてどう表現するか」をコードとコメントで一致させた。 +- `src/mir/phi_core/header_phi_builder.rs` に `NYASH_JOINIR_HEADER_EXP=1` フラグチェックを追加(現在はログのみ、挙動は不変)。 +- JoinIR テストまわり: + - skip_ws / min / trim の JoinIR 型・変換テストを維持しつつ、trim 側は `trim_main + loop_step + skip_leading` の 3 関数構成にテスト期待を合わせた。 + - `LoopHeaderShape` 用のミニテストを追加し、「to_loop_step_params() は pinned→carriers 順で返す」という契約を固定。 + +**状態** +- JoinIR 側では Header φ の意味が LoopHeaderShape で表現され、対象ループの `loop_step` 引数に反映済み。 +- HeaderPhiBuilder は従来挙動のまま(Phase 27.4-C 以降でトグル付き縮退を予定)。 +- すべての JoinIR テスト 7/7 が PASS、既存本線テストの緑度には影響なし。 + +--- + +### 1-03. Phase 27.4-C — HeaderPhiBuilder バイパス実験(JoinIR 経路限定、2025-11-23 完了) + +**目的** +- Header φ の意味は JoinIR 側(LoopHeaderShape+loop_step 引数)に持ち上がったので、JoinIR 実験経路に限って Rust 側 HeaderPhiBuilder をバイパスできるか試す。 +- 本線 MIR/LoopForm→VM の挙動には一切触れず、JoinIR runner テスト専用の縮退ステップとして運用する。 + +**やったこと** +- `src/mir/phi_core/header_phi_builder.rs` に 2 つのヘルパーを追加: + - `joinir_header_experiment_enabled()` … `NYASH_JOINIR_HEADER_EXP=1` チェック(27.4-B で導入)。 + - `joinir_header_bypass_enabled()` … `NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_HEADER_EXP=1` の両方が ON のとき true。 +- `src/mir/loop_builder.rs` で HeaderPhiBuilder 利用箇所にバイパスロジックを追加: + - 現在の関数名が `Main.skip/1` または `FuncScannerBox.trim/1` のとき、 + - かつ `joinir_header_bypass_enabled()` が true のときのみ `emit_header_phis()` をスキップ。 + - `NYASH_LOOPFORM_DEBUG=1` 時はデバッグログを出す。 +- JoinIR テスト側(skip_ws / trim)に、「NYASH_JOINIR_HEADER_EXP=1 を併用すると Header φ bypass が有効化される」旨のコメントを追加。 +- `docs/private/roadmap2/phases/phase-27-joinir/IMPLEMENTATION_LOG.md` に Phase 27.4-C セクションを追加し、対象関数・トグル条件・挙動を記録。 +- `phase-27-joinir/TASKS.md` で 27.4-C を完了扱いに更新。 + +**状態** +- JoinIR runner 実験時に `NYASH_JOINIR_EXPERIMENT=1` + `NYASH_JOINIR_HEADER_EXP=1` を立てた場合のみ、Main.skip/1 / FuncScannerBox.trim/1 の Header φ がスキップされる。 +- このモードでは VM 実行は使わず、JoinIR runner だけで意味が保たれているかを確認するテストとして運用。 +- 本線 MIR/LoopForm→VM の挙動は、トグル OFF 時には従来どおり(Header φ あり)のまま。 + +--- + +### 1-04. Phase 27.5 — JoinIR Exit φ 統合(設計着手、2025-11-24 現在) + +**目的** +- ExitPhiBuilder が担っている「exit φ(break/early-exit の合流)」の意味を、JoinIR 側の `k_exit` 呼び出し+引数として表現できるようにする設計フェーズ。 +- Rust 側の ExitPhiBuilder 挙動は変えず、本線 VM を壊さないまま minimal/trim の 2 ケースで Exit φ の意味を整理する。 + +**やったこと(設計メモ反映済み)** +- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/README.md` を追加し、minimal_ssa_skip_ws と FuncScanner.trim_min の exit 経路を整理。 + - minimal: break 経路は `i>=n` / `ch!=" "`, Exit φ は `i` だけ → JoinIR では `k_exit(i)` を想定。 + - trim: break 経路は `!(e>b)` / `!is_space`, Carrier=`e`, Pinned=`str,b`。ExitShape は Option A: `[e]`(第一候補) / Option B: `[str,b,e]` を比較と記述。 +- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/TASKS.md` の A-1/A-2 を完了にし、B 以降(LoopExitShape 型/コメント追加、JoinIR 変換への反映、テスト/ログ追記)はこれから。 + +**次の一手** +- LoopExitShape の型 or コメントを JoinIR に追加して、minimal/trim の exit 引数セットを明示。 +- JoinIR 変換の exit 部分に「k_exit で何を合流させるか」のコメントを足し、必要なら命令並びを軽く整える(意味は変えない)。 +- JoinIR テストと IMPLEMENTATION_LOG に Exit φ 観点のメモを追記。 + +--- + +### 1-05. Phase 26-F — Loop Exit Liveness / BodyLocal PHI Guard(箱とガードの整備、2025-11-22 時点) **目的** - LoopForm v2 / Exit PHI まわりで、BodyLocal 変数の未定義利用を「箱」と「Fail-Fast」で確実に検知・抑制できるようにする。 diff --git a/docs/private b/docs/private index 7025d50f..1e06e626 160000 --- a/docs/private +++ b/docs/private @@ -1 +1 @@ -Subproject commit 7025d50ff4de3d20ed3a330fb0aec910c4264683 +Subproject commit 1e06e626a2a36abcfae9dcdf73e06adf4ca44644 diff --git a/src/mir/join_ir.rs b/src/mir/join_ir.rs index 11ef113d..6d04f1e1 100644 --- a/src/mir/join_ir.rs +++ b/src/mir/join_ir.rs @@ -81,6 +81,35 @@ impl LoopHeaderShape { } } +/// Phase 27.5: ループ exit φ の意味を表す構造 +/// +/// ExitPhiBuilder が生成していた「ループ脱出時の変数合流」を JoinIR の k_exit 引数として表現するためのヘルパー。 +/// +/// 用語: +/// - **exit_args**: ループから脱出する際に k_exit に渡す値のリスト +/// +/// 例: +/// - **minimal_ssa_skip_ws**: exit_args = [i] +/// - ループから抜ける時、現在の i の値を返す +/// - **FuncScanner.trim**: exit_args = [e] (Option A) +/// - ループから抜ける時、現在の e の値を返す(後続で substring(b, e) を呼ぶ) +/// +/// Phase 27.5 では minimal/trim 用に手動で構成するが、将来は ExitPhiBuilder の分析から自動導出する。 +#[derive(Debug, Clone)] +#[allow(dead_code)] // Phase 27.6 で Exit φ 統合の実装フェーズで使用予定(現在は設計の雛形) +struct LoopExitShape { + /// Exit 時に k_exit に渡したい値(JoinIR 引数) + exit_args: Vec, +} + +#[allow(dead_code)] // Phase 27.6 で実際に使用予定 +impl LoopExitShape { + /// Phase 27.5: 手動で exit_args を指定して構築 + fn new_manual(exit_args: Vec) -> Self { + LoopExitShape { exit_args } + } +} + /// JoinIR 関数 #[derive(Debug, Clone)] pub struct JoinFunction { @@ -493,10 +522,14 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option= n { return i } loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), - args: vec![i_loop], + args: vec![i_loop], // ← LoopExitShape.exit_args に対応 cond: Some(cmp1_result), }); @@ -550,10 +583,11 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option Optio op: CompareOp::Eq, })); + // Phase 27.5: Exit φ の意味を LoopExitShape で明示(Option A) + // trim のループ脱出時は e の値で substring(b, e) を計算済み + let _exit_shape_trim = LoopExitShape::new_manual(vec![e_loop]); // exit_args = [e] (Option A) + // 実装上は既に trimmed_base = substring(b, e) を計算済みで、その結果を返している + // if !(e > b) { return substring(b, e) } loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), - args: vec![trimmed_base], + args: vec![trimmed_base], // ← substring(b, e) の結果 cond: Some(cond_is_false), }); @@ -870,10 +909,11 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio op: CompareOp::Eq, })); + // Phase 27.5: 2箇所目の exit パス(同じく exit_args = [e], Option A) // if !is_space { return substring(b, e) } loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(1), - args: vec![trimmed_base], + args: vec![trimmed_base], // ← substring(b, e) の結果(1箇所目と同じ) cond: Some(is_space_false), }); diff --git a/src/tests/mir_joinir_funcscanner_trim.rs b/src/tests/mir_joinir_funcscanner_trim.rs index 767d71b5..770d93e6 100644 --- a/src/tests/mir_joinir_funcscanner_trim.rs +++ b/src/tests/mir_joinir_funcscanner_trim.rs @@ -15,6 +15,11 @@ // - NYASH_JOINIR_HEADER_EXP=1 を併用すると Header φ bypass が有効化される // - bypass 時は MIR に Header φ が生成されないが、このテストでは JoinIR のみ検証するため問題なし // - 将来的に JoinIR runner 実行を追加する際は、bypass モードでも正しく動作することを確認する +// +// Phase 27.5 対応: +// - このテストは Header φ だけでなく、Exit φ(e の合流+substring(b, e) 呼び出し)も JoinIR で k_exit として表現できることを検証 +// - trim のループには2箇所の break パスがあり、どちらも substring(b, e) の結果を返す +// - ExitShape Option A として設計: exit_args = [e] で、ループ内で substring(b, e) を計算済み use crate::ast::ASTNode; use crate::mir::join_ir::*; diff --git a/src/tests/mir_joinir_skip_ws.rs b/src/tests/mir_joinir_skip_ws.rs index 8b2c03ce..797c20d4 100644 --- a/src/tests/mir_joinir_skip_ws.rs +++ b/src/tests/mir_joinir_skip_ws.rs @@ -15,6 +15,10 @@ // - NYASH_JOINIR_HEADER_EXP=1 を併用すると Header φ bypass が有効化される // - bypass 時は MIR に Header φ が生成されないが、このテストでは JoinIR のみ検証するため問題なし // - 将来的に JoinIR runner 実行を追加する際は、bypass モードでも正しく動作することを確認する +// +// Phase 27.5 対応: +// - このテストは Header φ だけでなく、Exit φ(i の合流)も JoinIR で k_exit(i) として表現できていることを検証 +// - skip_ws は2箇所の break パスがあり、どちらも i を返す → LoopExitShape::exit_args = [i] use crate::ast::ASTNode; use crate::mir::join_ir::*;