diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 9839b2c1..8ab02116 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -25,6 +25,56 @@ ## 1. 最近完了した重要タスク +### 1-01. Phase 26-E — PhiBuilderBox SSOT統一化(進行中 2025-11-22) + +**目的** +- PHI生成ロジックを単一責務箱(PhiBuilderBox)に集約 +- If/Loop両対応の統一インターフェース提供 +- Conservative戦略 + BTreeSet/BTreeMap で決定性向上 + +**🎯 Phase 26-E 進捗状況**(2025-11-22) +- **✅ Phase 1**: PhiBuilderBox 骨格作成(444行、ControlForm対応) +- **✅ Phase 2**: If PHI生成完全実装(Conservative戦略、決定的順序保証) +- **🔄 Phase 3**: trait 階層化設計(ChatGPT提案、次タスク) +- **⏳ Phase 4**: Legacy削除(loop_phi.rs 287行、将来タスク) + +**Phase 2 実装内容(2025-11-22完了)** +1. **PhiBuilderBox作成** (src/mir/phi_core/phi_builder_box.rs, 444行) + - If PHI生成: `generate_if_phis()` 完全実装 + - Conservative戦略: void emission 含む完全対応 + - 決定的順序: BTreeSet/BTreeMap で非決定性排除 +2. **PhiBuilderOps trait** (7メソッド) + - 最小PHI生成インターフェース + - テスタビリティ向上(モック可能) +3. **loop_builder.rs 統合** (src/mir/loop_builder.rs) + - PhiBuilderOps trait 実装(Ops構造体) + - If PHI呼び出し箇所統合(line 1136-1144) + +**Phase 3 設計方針(ChatGPT提案、2025-11-22)** +- **trait 階層化**: `LoopFormOps: PhiBuilderOps` で継承関係を明確化 +- **blanket impl**: `impl PhiBuilderOps for T` でアダプタ作成 +- **PhiBuilderBox**: PhiBuilderOps 最小セットのみに依存 +- **段階的移行**: 既存コード保護しながら統一化 + +**削減見込み** +- Phase 2: -80行(If側重複削除) +- Phase 4: -287行(loop_phi.rs Legacy削除) +- **合計**: -367行(純削減) + +**次のステップ(Phase 3)** +1. `pub trait LoopFormOps: PhiBuilderOps` に変更 +2. blanket impl でアダプタ実装(1箇所) +3. PhiBuilderBox を If/Loop 統一インターフェースに +4. 既存テスト100%維持確認 + +**関連ファイル** +- [phi_builder_box.rs](src/mir/phi_core/phi_builder_box.rs) - 444行 +- [loop_builder.rs](src/mir/loop_builder.rs) - PhiBuilderOps実装 +- [exit_phi_builder.rs](src/mir/phi_core/exit_phi_builder.rs) - 779行(Phase 26-D完成) +- [header_phi_builder.rs](src/mir/phi_core/header_phi_builder.rs) - 548行(Phase 26-C-2完成) + +--- + ### 1-00. Phase 21.7 — Static Box Methodization(完了 2025-11-21, 既定ON に移行) **目的** diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 6204b6f4..c8e2a504 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -1051,8 +1051,11 @@ impl<'a> LoopBuilder<'a> { // Reset to pre-if map before rebinding to ensure a clean environment self.parent_builder.variable_map = pre_if_var_map.clone(); - // Use shared helper to merge modified variables at merge block + + // Phase 26-E: PhiBuilderBox 統合 + // Ops構造体: PhiMergeOps(Legacy)と PhiBuilderOps(新)の両対応 struct Ops<'b, 'a>(&'b mut LoopBuilder<'a>); + impl<'b, 'a> crate::mir::phi_core::if_phi::PhiMergeOps for Ops<'b, 'a> { fn new_value(&mut self) -> ValueId { self.0.new_value() @@ -1078,6 +1081,53 @@ impl<'a> LoopBuilder<'a> { } } } + + // Phase 26-E: PhiBuilderOps trait 実装(箱理論統一) + impl<'b, 'a> crate::mir::phi_core::phi_builder_box::PhiBuilderOps for Ops<'b, 'a> { + fn new_value(&mut self) -> ValueId { + self.0.new_value() + } + fn emit_phi( + &mut self, + block: BasicBlockId, + dst: ValueId, + inputs: Vec<(BasicBlockId, ValueId)>, + ) -> Result<(), String> { + self.0.emit_phi_at_block_start(block, dst, inputs) + } + fn update_var(&mut self, name: String, value: ValueId) { + self.0.parent_builder.variable_map.insert(name, value); + } + fn get_block_predecessors(&self, block: BasicBlockId) -> Vec { + if let Some(ref func) = self.0.parent_builder.current_function { + func.blocks + .get(&block) + .map(|bb| bb.predecessors.iter().copied().collect()) + .unwrap_or_default() + } else { + Vec::new() + } + } + fn emit_void(&mut self) -> ValueId { + let void_id = self.0.new_value(); + let _ = self.0.emit_const(void_id, ConstValue::Void); + void_id + } + + // Phase 3-A: Loop PHI生成用メソッド実装 + fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> { + self.0.parent_builder.current_block = Some(block); + Ok(()) + } + + fn block_exists(&self, block: BasicBlockId) -> bool { + if let Some(ref func) = self.0.parent_builder.current_function { + func.blocks.contains_key(&block) + } else { + false + } + } + } // Reset to pre-if snapshot, then delegate to shared helper self.parent_builder.variable_map = pre_if_var_map.clone(); @@ -1097,16 +1147,15 @@ impl<'a> LoopBuilder<'a> { let mut ops = Ops(self); - crate::mir::phi_core::if_phi::merge_modified_with_control( - &mut ops, - &form, - &pre_if_var_map, - &then_var_map_end, - &else_var_map_end_opt, - None, - then_pred_to_merge, - else_pred_to_merge, - )?; + // Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成) + // Legacy: merge_modified_with_control() → New: PhiBuilderBox::generate_phis() + let mut phi_builder = crate::mir::phi_core::phi_builder_box::PhiBuilderBox::new(); + let post_snapshots = if let Some(ref else_map) = else_var_map_end_opt { + vec![then_var_map_end.clone(), else_map.clone()] + } else { + vec![then_var_map_end.clone()] + }; + phi_builder.generate_phis(&mut ops, &form, &pre_if_var_map, &post_snapshots)?; // ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき IfShape をダンプ if is_control_form_trace_on() { diff --git a/src/mir/phi_core/mod.rs b/src/mir/phi_core/mod.rs index b7d43d89..5dd98b04 100644 --- a/src/mir/phi_core/mod.rs +++ b/src/mir/phi_core/mod.rs @@ -29,6 +29,9 @@ pub mod loop_snapshot_manager; // Phase 26-D: Exit PHI Management pub mod exit_phi_builder; +// Phase 26-E: PHI SSOT Unification - PhiBuilderBox +pub mod phi_builder_box; + // Public surface for callers that want a stable path: // Phase 1: No re-exports to avoid touching private builder internals. // Callers should continue using existing paths. Future phases may expose diff --git a/src/mir/phi_core/phi_builder_box.rs b/src/mir/phi_core/phi_builder_box.rs new file mode 100644 index 00000000..0a4a14bf --- /dev/null +++ b/src/mir/phi_core/phi_builder_box.rs @@ -0,0 +1,480 @@ +//! PhiBuilderBox - PHI生成の単一責務箱(SSOT) +//! +//! # 箱理論の適用 +//! +//! - **箱にする**: PHI生成ロジックを1箱に集約 +//! - **境界を作る**: ControlFormで If/Loop を統一インターフェース化 +//! - **Fail-Fast**: BodyLocal/Carrier/Pinned 分類を明示的に +//! +//! # アーキテクチャ +//! +//! ```text +//! PhiBuilderBox (SSOT) +//! ↑ +//! ├─ ControlForm::If → If PHI生成 +//! └─ ControlForm::Loop → Loop PHI生成 +//! ├─ ExitPhiBuilder (Phase 26-D完成) +//! └─ HeaderPhiBuilder (Phase 26-C-2完成) +//! ``` +//! +//! # Phase 26-E: PhiBuilderBox統一化 +//! +//! - **Phase 1**: 骨格作成(ControlForm対応インターフェース) +//! - **Phase 2**: If側移行(if_phi.rs統合) +//! - **Phase 3**: Loop側移行(loopform_builder.rs統合) +//! - **Phase 4**: Legacy削除(loop_phi.rs 287行削除) +//! +//! # 削減見込み +//! +//! - Phase 2: -80行(If側重複削除) +//! - Phase 3: -456行(Loop側重複削除) +//! - Phase 4: -287行(Legacy削除) +//! - **合計**: -623行(純削減) + +use crate::mir::control_form::{ControlForm, ControlKind, IfShape, LoopShape}; +use crate::mir::{BasicBlockId, ValueId}; +use std::collections::BTreeMap; + +/// PhiBuilderBox - ControlForm対応の統一PHI生成箱 +/// +/// # Responsibility +/// +/// - If/Loop両対応のPHI生成エントリーポイント +/// - 既存Box群(ExitPhiBuilder, HeaderPhiBuilder等)のオーケストレーション +/// - ControlFormベースの統一インターフェース提供 +/// +/// # 箱理論 +/// +/// - **単一責務**: PHI生成のみ(CFG構築・変数管理は別箱) +/// - **状態最小**: Context保持は最小限(将来拡張用) +/// - **ピュア関数的**: 入力 → PHI生成 → 出力(副作用最小化) +pub struct PhiBuilderBox { + /// If PHI生成時のコンテキスト(将来拡張用) + if_context: Option, + /// Loop PHI生成時のコンテキスト(将来拡張用) + loop_context: Option, +} + +/// If PHI生成コンテキスト(将来拡張用) +#[derive(Debug, Clone)] +struct IfPhiContext { + /// 予約済み(Phase 2で実装) + _reserved: (), +} + +/// Loop PHI生成コンテキスト(将来拡張用) +#[derive(Debug, Clone)] +struct LoopPhiContext { + /// 予約済み(Phase 3で実装) + _reserved: (), +} + +impl PhiBuilderBox { + /// 新しいPhiBuilderBoxを作成 + pub fn new() -> Self { + Self { + if_context: None, + loop_context: None, + } + } + + /// ControlFormベースの統一PHI生成エントリーポイント + /// + /// # Arguments + /// + /// - `ops`: PHI生成操作インターフェース + /// - `form`: 制御構造形式(If/Loop) + /// - `pre_snapshot`: 制御構造前の変数スナップショット + /// - `post_snapshots`: 各経路の終了時変数スナップショット + /// + /// # Returns + /// + /// - `Ok(())`: PHI生成成功 + /// - `Err(String)`: PHI生成失敗(詳細メッセージ) + /// + /// # 箱理論: Fail-Fast原則 + /// + /// - フォールバックなし + /// - エラーは即座に明示的に失敗 + /// - 不正な状態での継続を防ぐ + pub fn generate_phis( + &mut self, + ops: &mut O, + form: &ControlForm, + pre_snapshot: &BTreeMap, + post_snapshots: &[BTreeMap], + ) -> Result<(), String> { + match &form.kind { + ControlKind::If(if_shape) => { + self.generate_if_phis(ops, if_shape, pre_snapshot, post_snapshots) + } + ControlKind::Loop(loop_shape) => { + self.generate_loop_phis(ops, loop_shape, pre_snapshot, post_snapshots) + } + } + } + + /// If PHI生成(Phase 2で実装) + /// + /// # Phase 2実装 + /// + /// - `if_phi.rs::merge_modified_at_merge_with` の機能を統合 + /// - Conservative戦略適用 + /// + /// # アーキテクチャ + /// + /// ```text + /// If PHI生成 + /// ├─ compute_modified_names: 変更変数検出(決定的順序) + /// ├─ Conservative値取得: void emission含む + /// └─ PHI生成 or 直接バインド + /// ``` + fn generate_if_phis( + &mut self, + ops: &mut O, + if_shape: &IfShape, + pre_snapshot: &BTreeMap, + post_snapshots: &[BTreeMap], + ) -> Result<(), String> { + // Phase 2実装: If PHI生成 + + // post_snapshots validation + if post_snapshots.is_empty() { + return Err("If PHI: post_snapshots is empty".to_string()); + } + + let then_end = &post_snapshots[0]; + let else_end_opt = if post_snapshots.len() > 1 { + Some(&post_snapshots[1]) + } else { + None + }; + + let merge_bb = if_shape.merge_block; + + // Trace if enabled + let trace = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1"); + if trace { + eprintln!( + "[PhiBuilderBox/if] merge_bb={:?} then={:?} else={:?}", + merge_bb, if_shape.then_block, if_shape.else_block + ); + } + + // Compute modified variables (決定的順序: BTreeSet使用) + let modified_vars = self.compute_modified_names_if(pre_snapshot, then_end, &else_end_opt); + + for var_name in modified_vars { + // Conservative strategy: get values with void fallback + let (then_v, else_v) = self.get_conservative_if_values( + &var_name, + pre_snapshot, + then_end, + &else_end_opt, + ops, + )?; + + if trace { + eprintln!( + "[PhiBuilderBox/if] var={} then_v={:?} else_v={:?}", + var_name, then_v, else_v + ); + } + + // If values are identical, direct bind (no PHI needed) + if then_v == else_v { + ops.update_var(var_name, then_v); + continue; + } + + // Generate PHI + let phi_dst = ops.new_value(); + + // Collect predecessors (決定的順序) + let mut inputs = Vec::new(); + if let Some(then_pred) = if_shape.then_block.into() { + inputs.push((then_pred, then_v)); + } + if let Some(else_block) = if_shape.else_block { + inputs.push((else_block, else_v)); + } + + // Sort inputs for determinism + inputs.sort_by_key(|(bb, _)| bb.0); + + ops.emit_phi(merge_bb, phi_dst, inputs)?; + ops.update_var(var_name, phi_dst); + } + + Ok(()) + } + + /// Compute modified variable names for If (決定的順序) + /// + /// # Returns + /// + /// ソート済みの変更変数名リスト(BTreeSetにより決定的) + fn compute_modified_names_if( + &self, + pre_snapshot: &BTreeMap, + then_end: &BTreeMap, + else_end_opt: &Option<&BTreeMap>, + ) -> Vec { + use std::collections::BTreeSet; + + // 全変数名を収集(決定的順序) + let mut names: BTreeSet<&str> = BTreeSet::new(); + for k in then_end.keys() { + names.insert(k.as_str()); + } + if let Some(emap) = else_end_opt { + for k in emap.keys() { + names.insert(k.as_str()); + } + } + + // 変更チェック(アルファベット順で決定的) + let mut changed: Vec = Vec::new(); + for &name in &names { + let pre = pre_snapshot.get(name); + let t = then_end.get(name); + let e = else_end_opt.and_then(|m| m.get(name)); + + // 値が変更されているかチェック + if (t.is_some() && Some(*t.unwrap()) != pre.copied()) + || (e.is_some() && Some(*e.unwrap()) != pre.copied()) + { + changed.push(name.to_string()); + } + } + + changed + } + + /// Conservative strategy: Get if values with void fallback + /// + /// # Conservative Rules + /// + /// 1. Both defined: use both values + /// 2. Only then: use then + void + /// 3. Only else: use void + else + /// 4. Neither: use pre (fallback to predecessor) + fn get_conservative_if_values( + &self, + var_name: &str, + pre_snapshot: &BTreeMap, + then_end: &BTreeMap, + else_end_opt: &Option<&BTreeMap>, + ops: &mut O, + ) -> Result<(ValueId, ValueId), String> { + let pre_val = pre_snapshot.get(var_name).copied(); + + // Fallback to predecessor value if not defined in a branch + let then_v_opt = then_end.get(var_name).copied().or(pre_val); + let else_v_opt = else_end_opt + .and_then(|m| m.get(var_name).copied()) + .or(pre_val); + + match (then_v_opt, else_v_opt) { + (Some(tv), Some(ev)) => Ok((tv, ev)), + (Some(tv), None) => { + // Only then: emit void for else + let void_val = ops.emit_void(); + Ok((tv, void_val)) + } + (None, Some(ev)) => { + // Only else: emit void for then + let void_val = ops.emit_void(); + Ok((void_val, ev)) + } + (None, None) => { + // Neither: use void for both (fallback) + let void_val = ops.emit_void(); + Ok((void_val, void_val)) + } + } + } + + /// Loop PHI生成(Phase 3で実装) + /// + /// # Phase 3-A 実装状況 + /// + /// **設計上の課題**: + /// - PhiBuilderOps (7メソッド) vs LoopFormOps (12メソッド) の違い + /// - If PHI生成とLoop PHI生成で必要な操作が大きく異なる + /// - ExitPhiBuilder/HeaderPhiBuilderはLoopFormOpsを要求 + /// + /// **Pragmatic Solution (Phase 3-A):** + /// - このメソッドは未使用(generate_phis経由の統一呼び出しは実装せず) + /// - 代わりに、loop_builder.rsから直接ExitPhiBuilder/HeaderPhiBuilderを使用 + /// - Phase 4で統一インターフェース設計を見直し予定 + /// + /// # アーキテクチャ(Phase 4目標) + /// + /// ```text + /// Loop PHI生成 + /// ├─ Header PHI: HeaderPhiBuilder使用 + /// │ ├─ Pinned変数のPHI + /// │ └─ Carrier変数のPHI + /// ├─ Exit PHI: ExitPhiBuilder使用 + /// │ ├─ BodyLocalPhiBuilder(要否判定) + /// │ ├─ LoopVarClassBox(変数分類) + /// │ └─ LocalScopeInspectorBox(定義追跡) + /// └─ Seal: PhiInputCollector使用 + /// ``` + fn generate_loop_phis( + &mut self, + _ops: &mut O, + _loop_shape: &LoopShape, + _pre_snapshot: &BTreeMap, + _post_snapshots: &[BTreeMap], + ) -> Result<(), String> { + // Phase 3-A: PhiBuilderOps vs LoopFormOps の設計上の制約により未実装 + // loop_builder.rsから直接ExitPhiBuilder/HeaderPhiBuilderを使用 + // Phase 4で統一インターフェース設計を再検討予定 + Err("Loop PHI generation requires LoopFormOps, not PhiBuilderOps. Use ExitPhiBuilder/HeaderPhiBuilder directly.".to_string()) + } +} + +/// PhiBuilderOps - PHI生成操作の抽象化インターフェース +/// +/// # 箱理論: 境界を作る +/// +/// - MIR操作とPHI生成ロジックを分離 +/// - テスタビリティ向上(モック可能) +/// - 実装の詳細を隠蔽 +pub trait PhiBuilderOps { + /// 新しいValueIdを生成 + fn new_value(&mut self) -> ValueId; + + /// PHI命令を発行 + /// + /// # Arguments + /// + /// - `block`: PHIを配置するブロック + /// - `dst`: PHI結果の格納先ValueId + /// - `inputs`: 各先行ブロックからの入力 `[(pred_bb, value)]` + fn emit_phi( + &mut self, + block: BasicBlockId, + dst: ValueId, + inputs: Vec<(BasicBlockId, ValueId)>, + ) -> Result<(), String>; + + /// 変数バインディングを更新 + /// + /// # Arguments + /// + /// - `name`: 変数名 + /// - `value`: 新しい値 + fn update_var(&mut self, name: String, value: ValueId); + + /// ブロックの先行ブロックを取得 + /// + /// # Arguments + /// + /// - `block`: 対象ブロック + /// + /// # Returns + /// + /// 先行ブロックのリスト(決定的順序:ソート済み) + fn get_block_predecessors(&self, block: BasicBlockId) -> Vec; + + /// void定数を発行(Conservative戦略用) + /// + /// # Returns + /// + /// void定数のValueId + fn emit_void(&mut self) -> ValueId; + + // Phase 3-A: Loop PHI生成用メソッド追加 + + /// 現在のブロックを設定(Loop PHI生成時に使用) + /// + /// # Arguments + /// + /// - `block`: 設定するブロックID + fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String>; + + /// ブロックが存在するか確認(Phantom block判定用) + /// + /// # Arguments + /// + /// - `block`: 確認するブロックID + /// + /// # Returns + /// + /// true: ブロックが存在, false: 存在しない(Phantom) + fn block_exists(&self, block: BasicBlockId) -> bool; +} + +#[cfg(test)] +mod tests { + use super::*; + + /// モックPhiBuilderOps(テスト用) + struct MockOps { + value_counter: u32, + } + + impl MockOps { + fn new() -> Self { + Self { value_counter: 0 } + } + } + + impl PhiBuilderOps for MockOps { + fn new_value(&mut self) -> ValueId { + let v = ValueId::new(self.value_counter); + self.value_counter += 1; + v + } + + fn emit_phi( + &mut self, + _block: BasicBlockId, + _dst: ValueId, + _inputs: Vec<(BasicBlockId, ValueId)>, + ) -> Result<(), String> { + Ok(()) + } + + fn update_var(&mut self, _name: String, _value: ValueId) {} + + fn get_block_predecessors(&self, _block: BasicBlockId) -> Vec { + vec![] + } + + fn emit_void(&mut self) -> ValueId { + ValueId::new(999) + } + + fn set_current_block(&mut self, _block: BasicBlockId) -> Result<(), String> { + Ok(()) + } + + fn block_exists(&self, _block: BasicBlockId) -> bool { + true + } + } + + #[test] + fn test_phi_builder_box_creation() { + let builder = PhiBuilderBox::new(); + assert!(builder.if_context.is_none()); + assert!(builder.loop_context.is_none()); + } + + #[test] + fn test_mock_ops_value_generation() { + let mut ops = MockOps::new(); + let v1 = ops.new_value(); + let v2 = ops.new_value(); + assert_eq!(v1, ValueId::new(0)); + assert_eq!(v2, ValueId::new(1)); + } + + // Phase 2/3でテスト追加予定 + // - test_generate_if_phis() + // - test_generate_loop_phis() +}