feat(phase27): Phase 27.4-A JoinIR Header φ Integration

Phase 27.4-A実装完了: JoinIR側でloop header φの意味をPinned/Carrier情報から再構成

主な変更:
- src/mir/join_ir.rs: LoopHeaderShape構造体追加
  - Pinned: ループ中で不変の変数(例: skip_ws の s, n / trim の str, b)
  - Carrier: ループで更新される変数(例: skip_ws の i / trim の e)
  - to_loop_step_params()メソッドで引数リスト生成

- lower_skip_ws_to_joinir(), lower_funcscanner_trim_to_joinir():
  - Pinned/Carrier構造をコメントで明示
  - _header_shape変数で将来の自動導出の雛形を準備

- src/mir/phi_core/header_phi_builder.rs: 実験フラグ追加
  - joinir_header_experiment_enabled(): NYASH_JOINIR_HEADER_EXP=1チェック
  - new()でフラグ有効時にログ出力(挙動変更なし)
  - Phase 27.4移行計画をモジュールドキュメントに記載

テスト結果:
-  mir_joinir_skip_ws_auto_lowering PASS
-  mir_joinir_min_auto_lowering PASS
-  全type_sanityテスト PASS
- ⚠️ mir_joinir_funcscanner_trim_auto_lowering FAIL (既存問題、本実装と無関係)

原則: 本線MIR/LoopForm→VMの挙動は一切変更なし。JoinIRはトグル付き実験経路。
This commit is contained in:
nyash-codex
2025-11-23 09:23:13 +09:00
parent c652d01b8f
commit 8db5e59f6f
2 changed files with 91 additions and 7 deletions

View File

@ -43,6 +43,37 @@ impl JoinContId {
/// 変数IDPhase 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<ValueId>,
/// Carrier: ループで更新される変数リスト(φ ノードで合流が必要)
carriers: Vec<ValueId>,
}
impl LoopHeaderShape {
/// Phase 27.4-A: 手動で Pinned/Carrier を指定して構築
fn new_manual(pinned: Vec<ValueId>, carriers: Vec<ValueId>) -> Self {
LoopHeaderShape { pinned, carriers }
}
/// loop_step 関数の引数リストを生成pinned → carrier の順)
fn to_loop_step_params(&self) -> Vec<ValueId> {
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<JoinMod
join_module.entry = Some(skip_id);
join_module.add_function(skip_func);
// Phase 27.4-A: loop_step の Pinned/Carrier 構造を明示
// skip_ws ループの場合:
// - Pinned: s (文字列), n (長さ) - ループ中で不変
// - Carrier: i (現在位置) - ループで更新される
let s_loop = ValueId(4000); // Pinned
let i_loop = ValueId(4001); // Carrier
let n_loop = ValueId(4002); // Pinned
let _header_shape = LoopHeaderShape::new_manual(
vec![s_loop, n_loop], // Pinned: s, n
vec![i_loop], // Carrier: i
);
// Phase 27.4: loop_step 引数は header_shape.to_loop_step_params() で生成可能だが、
// 今回は互換性のため手動の順序 [s, i, n] を維持pinned/carrier が混在)
// loop_step 関数: if i >= 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(),

View File

@ -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()
}