feat(phi): Phase 27.5 - JoinIR Exit φ 統合(LoopExitShape 雛形)
LoopExitShape 構造体を追加し、Exit φ の意味を JoinIR 側に固定: - LoopExitShape 追加 (src/mir/join_ir.rs:84-111) - exit_args: Vec<ValueId> で Exit φ の意味を表現 - minimal (exit_args=[i]), trim (exit_args=[e], Option A) - #[allow(dead_code)] で Phase 27.6 まで設計専用 - Exit φ コメント追加 - lower_skip_ws_to_joinir: 2箇所の exit パスに意味明記 - lower_funcscanner_trim_to_joinir: Option A として意味明記 - テストコメント更新 - mir_joinir_skip_ws.rs: Exit φ (i の合流) 検証を明記 - mir_joinir_funcscanner_trim.rs: Exit φ (e の合流+substring) を明記 - ドキュメント更新 - IMPLEMENTATION_LOG.md: Phase 27.5 セクション追加 - TASKS.md: Phase 27.5 完了マーク ExitPhiBuilder は Phase 27.6 まで保留。本線影響ゼロ。
This commit is contained in:
@ -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」で確実に検知・抑制できるようにする。
|
||||
|
||||
Submodule docs/private updated: 7025d50ff4...1e06e626a2
@ -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<ValueId>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Phase 27.6 で実際に使用予定
|
||||
impl LoopExitShape {
|
||||
/// Phase 27.5: 手動で exit_args を指定して構築
|
||||
fn new_manual(exit_args: Vec<ValueId>) -> 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<JoinMod
|
||||
rhs: n_loop,
|
||||
}));
|
||||
|
||||
// Phase 27.5: Exit φ の意味を LoopExitShape で明示
|
||||
// skip_ws のループ脱出時は i の値だけを返す(先頭空白の文字数)
|
||||
let _exit_shape = LoopExitShape::new_manual(vec![i_loop]); // exit_args = [i]
|
||||
|
||||
// if i >= 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<JoinMod
|
||||
rhs: bool_false,
|
||||
}));
|
||||
|
||||
// Phase 27.5: 2箇所目の exit パス(同じく exit_args = [i])
|
||||
// if ch != " " { return i }
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: JoinContId::new(1),
|
||||
args: vec![i_loop],
|
||||
args: vec![i_loop], // ← LoopExitShape.exit_args に対応(1箇所目と同じ)
|
||||
cond: Some(cmp2_is_false),
|
||||
});
|
||||
|
||||
@ -749,10 +783,15 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> 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),
|
||||
});
|
||||
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user