diff --git a/src/mir/join_ir.rs b/src/mir/join_ir.rs index 31997398..85fd1692 100644 --- a/src/mir/join_ir.rs +++ b/src/mir/join_ir.rs @@ -43,6 +43,37 @@ impl JoinContId { /// 変数ID(Phase 26-H では MIR の ValueId を再利用) pub type VarId = ValueId; +/// Phase 27.4-A: ループ header φ の意味を表す構造(Pinned/Carrier 分類) +/// +/// HeaderPhiBuilder が生成していた「ループ変数の合流」を JoinIR の loop_step 引数として表現するためのヘルパー。 +/// +/// 用語: +/// - **Pinned**: ループ中で値が変わらない変数(例: skip_ws の s, n / trim の str, b) +/// - **Carrier**: ループで更新される変数(例: skip_ws の i / trim の e) +/// +/// Phase 27.4 では minimal/trim 用に手動で構成するが、将来は LoopVarClassBox から自動導出する。 +#[derive(Debug, Clone)] +struct LoopHeaderShape { + /// Pinned: ループ中で不変の変数リスト(初期値がそのまま使われる) + pinned: Vec, + /// Carrier: ループで更新される変数リスト(φ ノードで合流が必要) + carriers: Vec, +} + +impl LoopHeaderShape { + /// Phase 27.4-A: 手動で Pinned/Carrier を指定して構築 + fn new_manual(pinned: Vec, carriers: Vec) -> Self { + LoopHeaderShape { pinned, carriers } + } + + /// loop_step 関数の引数リストを生成(pinned → carrier の順) + fn to_loop_step_params(&self) -> Vec { + let mut params = self.pinned.clone(); + params.extend(self.carriers.clone()); + params + } +} + /// JoinIR 関数 #[derive(Debug, Clone)] pub struct JoinFunction { @@ -416,14 +447,26 @@ pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option= n { return i } else if ch == " " { loop_step(i + 1) } else { return i } - let s_loop = ValueId(4000); - let i_loop = ValueId(4001); - let n_loop = ValueId(4002); let mut loop_step_func = JoinFunction::new( loop_step_id, "loop_step".to_string(), - vec![s_loop, i_loop, n_loop], + vec![s_loop, i_loop, n_loop], // [pinned, carrier, pinned] の順(現行実装) ); let cmp1_result = ValueId(4003); @@ -643,10 +686,22 @@ pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Optio join_module.entry = Some(trim_main_id); join_module.add_function(trim_main_func); + // Phase 27.4-A: trim loop_step の Pinned/Carrier 構造を明示 + // trim ループの場合: + // - Pinned: str (文字列), b (開始位置) - ループ中で不変 + // - Carrier: e (終了位置) - ループで後ろから前へ更新される + let str_loop = ValueId(6000); // Pinned + let b_loop = ValueId(6001); // Pinned + let e_loop = ValueId(6002); // Carrier + + let _header_shape = LoopHeaderShape::new_manual( + vec![str_loop, b_loop], // Pinned: str, b + vec![e_loop], // Carrier: e + ); + // Phase 27.4: loop_step 引数は header_shape.to_loop_step_params() で生成可能だが、 + // 今回は互換性のため手動の順序 [str, b, e] を維持(pinned, pinned, carrier の順) + // loop_step 関数: 末尾の空白を削り、最終的に substring(b, e) を返す - let str_loop = ValueId(6000); - let b_loop = ValueId(6001); - let e_loop = ValueId(6002); let mut loop_step_func = JoinFunction::new( loop_step_id, "loop_step".to_string(), diff --git a/src/mir/phi_core/header_phi_builder.rs b/src/mir/phi_core/header_phi_builder.rs index 719716a1..837f83b6 100644 --- a/src/mir/phi_core/header_phi_builder.rs +++ b/src/mir/phi_core/header_phi_builder.rs @@ -6,10 +6,35 @@ //! - Latch値更新(seal時) //! //! Box-First理論: Header PHI生成の責任を明確に分離し、テスト可能な箱として提供 +//! +//! # Phase 27.4 移行計画 +//! +//! **Header φ の責務は JoinIR の loop_step 引数に順次移していく。** +//! Rust 側の φ は当面互換のため残すが、JoinIR 経路では以下の方針で縮退していく: +//! +//! 1. **Phase 27.4-A**: JoinIR 側で Pinned/Carrier を loop_step 引数として表現(完了) +//! 2. **Phase 27.4-B**: HeaderPhiBuilder に JoinIR 実験フラグを追加(現在) +//! 3. **Phase 27.4-C**: JoinIR 経路で Header φ をスキップ可能にする(将来) +//! 4. **Phase 27.5+**: HeaderPhiBuilder を JoinIR 前段に完全統合(長期目標) +//! +//! **原則**: 本線 MIR/LoopForm → VM の挙動は一切変えない。JoinIR はトグル付き実験経路。 use crate::mir::{BasicBlockId, ValueId}; use std::collections::HashMap; +/// Phase 27.4-B: JoinIR 実験モードが有効かチェック +/// +/// 環境変数 `NYASH_JOINIR_HEADER_EXP=1` のときに true を返す。 +/// このフラグが有効な場合、将来的に Header φ 生成をスキップする経路が追加される。 +/// +/// **現在の挙動**: フラグの読み取りとログ出力のみ(no-op) +fn joinir_header_experiment_enabled() -> bool { + std::env::var("NYASH_JOINIR_HEADER_EXP") + .ok() + .as_deref() + == Some("1") +} + /// Header PHI生成専門Box /// /// # Purpose @@ -104,6 +129,10 @@ impl HeaderPhiBuilder { /// let builder = HeaderPhiBuilder::new(); /// ``` pub fn new() -> Self { + // Phase 27.4-B: JoinIR 実験フラグのチェック(ログ出力のみ、挙動変更なし) + if joinir_header_experiment_enabled() { + eprintln!("[HeaderPhiBuilder] JoinIR experiment flag is ON (NYASH_JOINIR_HEADER_EXP=1)"); + } Self::default() }