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:
nyash-codex
2025-11-23 11:03:38 +09:00
parent 0d3d6cc455
commit 4fd74f2a6e
5 changed files with 123 additions and 7 deletions

View File

@ -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),
});

View File

@ -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::*;

View File

@ -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::*;