feat(phi): Phase 27.6-2 - ExitPhiBuilder バイパス実装

ExitPhiBuilder にトグル付きバイパスを追加:

- ヘルパー関数追加 (exit_phi_builder.rs:397-419)
  - joinir_exit_bypass_enabled(): トグルチェック
  - is_joinir_exit_bypass_target(): 対象関数判定
  - 環境変数: NYASH_JOINIR_EXPERIMENT=1 + NYASH_JOINIR_EXIT_EXP=1

- バイパス実装 (loop_builder.rs:657-692)
  - Exit φ 生成前にバイパス判定
  - 対象関数: Main.skip/1, FuncScannerBox.trim/1
  - ログ: [loopform/exit-bypass] func=... exit=... header=...

- Header φ バイパス(27.4-C)と対称的な実装
  - 両方のトグルで Header/Exit φ の完全スキップが可能
  - JoinIR 実験経路専用(本線影響ゼロ)

- 動作確認
  - トグル OFF: 372 passed; 9 failed(既存と一致)
  - コンパイル: エラーゼロ 

本線影響ゼロ。次は Phase 27.6-3 で A/B テスト。
This commit is contained in:
nyash-codex
2025-11-23 11:26:42 +09:00
parent 4fd74f2a6e
commit 52b56efb47
2 changed files with 60 additions and 11 deletions

View File

@ -654,17 +654,42 @@ impl<'a> LoopBuilder<'a> {
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
// [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds)
// - Case A: header+break → exit PHI includes both paths
// - Case B: break-only → exit PHI excludes header (not a predecessor)
let exit_snaps = self.exit_snapshots.clone();
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
&loopform,
self,
&form,
&exit_snaps,
branch_source_block,
)?;
// Phase 27.6-2: JoinIR Exit φ バイパスチェック
let fn_name = self
.parent_builder
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("");
let exit_bypass = crate::mir::phi_core::exit_phi_builder::joinir_exit_bypass_enabled()
&& crate::mir::phi_core::exit_phi_builder::is_joinir_exit_bypass_target(fn_name);
if exit_bypass {
// Phase 27.6-2: JoinIR 実験経路では Exit φ を生成しない。
// ループ内で定義された値だけで exit 後を構成するJoinIR の k_exit 引数として表現)。
//
// ⚠️ 重要: このモードでは MIR は不完全Exit φ 抜けであり、VM で実行できない。
// JoinIR runner 専用モードであることに注意。
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!(
"[loopform/exit-bypass] func={} exit={:?} header={:?} (JoinIR experiment only)",
fn_name, exit_id, header_id
);
}
} else {
// [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds)
// - Case A: header+break → exit PHI includes both paths
// - Case B: break-only → exit PHI excludes header (not a predecessor)
let exit_snaps = self.exit_snapshots.clone();
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
&loopform,
self,
&form,
&exit_snaps,
branch_source_block,
)?;
}
// Pop loop context
crate::mir::builder::loops::pop_loop_context(self.parent_builder);

View File

@ -394,6 +394,30 @@ impl<T: crate::mir::phi_core::loopform_builder::LoopFormOps> LoopFormOps for T {
}
}
// ============================================================================
// Phase 27.6-2: JoinIR Exit φ バイパス用ヘルパー関数
// ============================================================================
/// JoinIR Exit φ 縮退実験トグル
///
/// - NYASH_JOINIR_EXPERIMENT=1
/// - NYASH_JOINIR_EXIT_EXP=1
///
/// の両方が立っているときだけ true。
pub(crate) fn joinir_exit_bypass_enabled() -> bool {
crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_EXPERIMENT")
&& crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_EXIT_EXP")
}
/// Exit φ バイパス対象の関数かどうか
///
/// 当面は minimal/trim の 2 本だけ:
/// - Main.skip/1
/// - FuncScannerBox.trim/1
pub(crate) fn is_joinir_exit_bypass_target(func_name: &str) -> bool {
matches!(func_name, "Main.skip/1" | "FuncScannerBox.trim/1")
}
// ============================================================================
// Unit Tests
// ============================================================================