//! Phase 61-3: If-in-loop PHI Emitter //! //! ループ内 if の PHI 生成を担当する「箱」。 //! JoinIR + PhiSpec から直接 PHI 命令を発行する。 //! //! ## 箱理論における位置づけ //! //! ```text //! ┌─────────────────────────────────────────────────────────────┐ //! │ if_lowering.rs(オーケストレーター) │ //! │ ↓ │ //! │ ┌─────────────────────┐ ┌─────────────────────────────┐ │ //! │ │ JoinIR Lowering │ │ IfInLoopPhiEmitter │ │ //! │ │ (PhiSpec生成) │→ │ (PHI命令発行) │ │ //! │ └─────────────────────┘ └─────────────────────────────┘ │ //! └─────────────────────────────────────────────────────────────┘ //! ``` //! //! ## 責務 //! //! - Header PHI 生成(ループイテレーション継続用) //! - VarLookup 方式による incoming 値解決 //! - PhiBuilderOps trait 経由での PHI 発行 //! //! ## 設計原則 //! //! - **Thin Box**: ロジックは最小限、組み立てに専念 //! - **CFG非依存**: incoming値は snapshot から直接取得 //! - **決定性保証**: BTreeSet/BTreeMap のみ使用 use crate::mir::control_form::IfShape; use crate::mir::join_ir::lowering::if_phi_spec::PhiSpec; use crate::mir::phi_core::phi_builder_box::PhiBuilderOps; use crate::mir::ValueId; use std::collections::{BTreeMap, BTreeSet}; /// If-in-loop PHI Emitter /// /// JoinIR経路で生成された PhiSpec を元に、 /// 実際の PHI 命令を発行する。 pub struct IfInLoopPhiEmitter; impl IfInLoopPhiEmitter { /// Header PHI 生成 /// /// ループキャリア変数に対して、then/else 腕の値を統合する PHI を生成する。 /// /// # Arguments /// /// * `phi_spec` - JoinIR から計算された PHI 仕様 /// * `pre_if_var_map` - if 直前の変数マップ(VarLookup) /// * `then_snapshot` - then 腕終了時の変数スナップショット /// * `else_snapshot_opt` - else 腕終了時の変数スナップショット(ない場合は None) /// * `carrier_names` - ループキャリア変数名(片腕 PHI 対象) /// * `ops` - PHI 生成操作インターフェース /// * `if_shape` - If 構造情報 /// /// # Returns /// /// 生成した PHI の数 pub fn emit_header_phis( phi_spec: &PhiSpec, pre_if_var_map: &BTreeMap, then_snapshot: &BTreeMap, else_snapshot_opt: Option<&BTreeMap>, carrier_names: &BTreeSet, ops: &mut O, if_shape: &IfShape, ) -> Result { let mut phi_count = 0; let trace_on = crate::config::env::joinir_dev_enabled() && std::env::var("HAKO_JOINIR_IF_IN_LOOP_TRACE") .ok() .as_deref() == Some("1"); if trace_on { eprintln!("[Phase 61-3] IfInLoopPhiEmitter::emit_header_phis start"); eprintln!("[Phase 61-3] header_phis: {:?}", phi_spec.header_phis); eprintln!("[Phase 61-3] carrier_names: {:?}", carrier_names); } // Header PHI 生成: carrier_names に含まれる変数のみ for var_name in &phi_spec.header_phis { if !carrier_names.contains(var_name) { continue; } // VarLookup: pre_if値を取得 let pre_val = match pre_if_var_map.get(var_name) { Some(&v) => v, None => { if trace_on { eprintln!( "[Phase 61-3] ⚠️ var={} not found in pre_if_var_map, skipping", var_name ); } continue; } }; // Then値: snapshot から取得、なければ pre_val let then_val = then_snapshot.get(var_name).copied().unwrap_or(pre_val); // Else値: snapshot から取得、なければ pre_val(片腕 PHI パターン) let else_val = else_snapshot_opt .and_then(|m| m.get(var_name).copied()) .unwrap_or(pre_val); // 値が同一なら PHI 不要 if then_val == else_val { if trace_on { eprintln!( "[Phase 61-3] var={}: then_val == else_val ({:?}), no PHI needed", var_name, then_val ); } ops.update_var(var_name.clone(), then_val); continue; } // PHI 生成 let phi_dst = ops.new_value(); let mut inputs = Vec::new(); // Then 入力 inputs.push((if_shape.then_block, then_val)); // Else 入力 if let Some(else_bb) = if_shape.else_block { inputs.push((else_bb, else_val)); } if trace_on { eprintln!( "[Phase 61-3] var={}: emit PHI dst={:?} inputs={:?}", var_name, phi_dst, inputs ); } ops.emit_phi(if_shape.merge_block, phi_dst, inputs)?; ops.update_var(var_name.clone(), phi_dst); phi_count += 1; } if trace_on { eprintln!( "[Phase 61-3] IfInLoopPhiEmitter::emit_header_phis done: {} PHIs", phi_count ); } Ok(phi_count) } /// Phase 61-4: ループ外 If PHI 生成(Toplevel If) /// /// ループ外の純粋な if に対して PHI を生成する。 /// carrier_names が空のため、PhiSpec の全変数を対象とする。 /// /// # Arguments /// /// * `phi_spec` - JoinIR から計算された PHI 仕様 /// * `pre_if_var_map` - if 直前の変数マップ /// * `then_snapshot` - then 腕終了時の変数スナップショット /// * `else_snapshot_opt` - else 腕終了時の変数スナップショット /// * `ops` - PHI 生成操作インターフェース /// * `if_shape` - If 構造情報 /// /// # Returns /// /// 生成した PHI の数 pub fn emit_toplevel_phis( phi_spec: &PhiSpec, pre_if_var_map: &BTreeMap, then_snapshot: &BTreeMap, else_snapshot_opt: Option<&BTreeMap>, ops: &mut O, if_shape: &IfShape, ) -> Result { let mut phi_count = 0; let trace_on = crate::config::env::joinir_dev_enabled() && std::env::var("HAKO_JOINIR_IF_TOPLEVEL_TRACE") .ok() .as_deref() == Some("1"); if trace_on { eprintln!("[Phase 61-4] IfInLoopPhiEmitter::emit_toplevel_phis start"); eprintln!("[Phase 61-4] header_phis: {:?}", phi_spec.header_phis); } // Toplevel PHI 生成: PhiSpec の全変数を対象 for var_name in &phi_spec.header_phis { // pre_if値を取得 let pre_val = match pre_if_var_map.get(var_name) { Some(&v) => v, None => { if trace_on { eprintln!( "[Phase 61-4] ⚠️ var={} not found in pre_if_var_map, skipping", var_name ); } continue; } }; // Then値: snapshot から取得、なければ pre_val let then_val = then_snapshot.get(var_name).copied().unwrap_or(pre_val); // Else値: snapshot から取得、なければ pre_val let else_val = else_snapshot_opt .and_then(|m| m.get(var_name).copied()) .unwrap_or(pre_val); // 値が同一なら PHI 不要 if then_val == else_val { if trace_on { eprintln!( "[Phase 61-4] var={}: then_val == else_val ({:?}), no PHI needed", var_name, then_val ); } ops.update_var(var_name.clone(), then_val); continue; } // PHI 生成 let phi_dst = ops.new_value(); let mut inputs = Vec::new(); // Then 入力 inputs.push((if_shape.then_block, then_val)); // Else 入力 if let Some(else_bb) = if_shape.else_block { inputs.push((else_bb, else_val)); } if trace_on { eprintln!( "[Phase 61-4] var={}: emit PHI dst={:?} inputs={:?}", var_name, phi_dst, inputs ); } ops.emit_phi(if_shape.merge_block, phi_dst, inputs)?; ops.update_var(var_name.clone(), phi_dst); phi_count += 1; } if trace_on { eprintln!( "[Phase 61-4] IfInLoopPhiEmitter::emit_toplevel_phis done: {} PHIs", phi_count ); } Ok(phi_count) } } #[cfg(test)] mod tests { use super::*; use crate::mir::BasicBlockId; /// モック PhiBuilderOps 実装 struct MockOps { next_id: u32, emitted_phis: Vec<(BasicBlockId, ValueId, Vec<(BasicBlockId, ValueId)>)>, var_updates: Vec<(String, ValueId)>, } impl MockOps { fn new() -> Self { Self { next_id: 1000, emitted_phis: Vec::new(), var_updates: Vec::new(), } } } impl PhiBuilderOps for MockOps { fn new_value(&mut self) -> ValueId { let id = self.next_id; self.next_id += 1; ValueId(id) } fn emit_phi( &mut self, block: BasicBlockId, dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)>, ) -> Result<(), String> { self.emitted_phis.push((block, dst, inputs)); Ok(()) } fn update_var(&mut self, name: String, value: ValueId) { self.var_updates.push((name, value)); } fn get_block_predecessors(&self, _block: BasicBlockId) -> Vec { Vec::new() } fn emit_void(&mut self) -> ValueId { self.new_value() } fn set_current_block(&mut self, _block: BasicBlockId) -> Result<(), String> { Ok(()) } fn block_exists(&self, _block: BasicBlockId) -> bool { true } } #[test] fn test_emit_header_phis_basic() { // Setup let mut phi_spec = PhiSpec::new(); phi_spec.header_phis.insert("x".to_string()); let mut pre_if = BTreeMap::new(); pre_if.insert("x".to_string(), ValueId(1)); let mut then_snap = BTreeMap::new(); then_snap.insert("x".to_string(), ValueId(2)); // then で変更 let else_snap = BTreeMap::new(); // else では変更なし let mut carriers = BTreeSet::new(); carriers.insert("x".to_string()); let if_shape = IfShape { cond_block: BasicBlockId(10), then_block: BasicBlockId(11), else_block: Some(BasicBlockId(12)), merge_block: BasicBlockId(13), }; let mut ops = MockOps::new(); // Execute let count = IfInLoopPhiEmitter::emit_header_phis( &phi_spec, &pre_if, &then_snap, Some(&else_snap), &carriers, &mut ops, &if_shape, ) .unwrap(); // Verify assert_eq!(count, 1); assert_eq!(ops.emitted_phis.len(), 1); let (block, _dst, inputs) = &ops.emitted_phis[0]; assert_eq!(*block, BasicBlockId(13)); // merge_block assert_eq!(inputs.len(), 2); assert_eq!(inputs[0], (BasicBlockId(11), ValueId(2))); // then: 変更値 assert_eq!(inputs[1], (BasicBlockId(12), ValueId(1))); // else: pre_val } #[test] fn test_emit_header_phis_same_value() { // 両腕で同じ値の場合、PHI不要 let mut phi_spec = PhiSpec::new(); phi_spec.header_phis.insert("x".to_string()); let mut pre_if = BTreeMap::new(); pre_if.insert("x".to_string(), ValueId(1)); let mut then_snap = BTreeMap::new(); then_snap.insert("x".to_string(), ValueId(1)); // 変更なし let else_snap = BTreeMap::new(); let mut carriers = BTreeSet::new(); carriers.insert("x".to_string()); let if_shape = IfShape { cond_block: BasicBlockId(10), then_block: BasicBlockId(11), else_block: Some(BasicBlockId(12)), merge_block: BasicBlockId(13), }; let mut ops = MockOps::new(); let count = IfInLoopPhiEmitter::emit_header_phis( &phi_spec, &pre_if, &then_snap, Some(&else_snap), &carriers, &mut ops, &if_shape, ) .unwrap(); assert_eq!(count, 0); // PHI不要 assert_eq!(ops.emitted_phis.len(), 0); assert_eq!(ops.var_updates.len(), 1); assert_eq!(ops.var_updates[0], ("x".to_string(), ValueId(1))); } }