refactor(phi): Phase 25.1q - Conservative PHI strategy unification

**Changes**:
1. Created phi_core/conservative.rs module
   - ConservativeMerge struct for PHI generation analysis
   - Unified Conservative strategy implementation
   - Box-First theory application

2. Refactored phi.rs merge_modified_vars
   - Use ConservativeMerge::analyze() instead of inline logic
   - Reduced from ~60 lines to ~35 lines (42% reduction)
   - Improved code clarity and maintainability

**Benefits**:
- Centralized Conservative PHI logic (easier to maintain)
- Eliminated duplicate variable union calculation
- Clear separation of concerns (analysis vs execution)
- Foundation for future PhiMergeHelper unification

**Testing**:
 mir_stage1_using_resolver_full_collect_entries_verifies passes
 Phase 25.1c/k SSA fix preserved
 MIR correctness verified

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-19 10:32:16 +09:00
parent f81d6e53a5
commit c27981c35a
3 changed files with 201 additions and 55 deletions

View File

@ -0,0 +1,166 @@
//! Conservative PHI Generation - Box Theory Application
//!
//! Theory: Conservative ∘ Elimination = Minimal SSA
//! - Conservative (this): correctness-first, generate all PHIs
//! - Elimination (future): efficiency optimization, remove unused PHIs
//!
//! Phase 25.1q: Conservative PHI戦略の一元化
//! - phi.rs の Conservative PHI ロジックL23-117を統一
//! - void emission、predecessor fallback の一貫性保証
use crate::mir::ValueId;
use std::collections::{HashMap, HashSet};
/// Conservative PHI 戦略による変数マージ分析
pub struct ConservativeMerge {
/// 全ブランチに存在する変数のユニオンConservative戦略
pub all_vars: HashSet<String>,
/// 実際に変更された変数のセット(デバッグ/ヒント用)
pub changed_vars: HashSet<String>,
}
impl ConservativeMerge {
/// 全変数のユニオンを計算Conservative戦略
///
/// # Arguments
/// * `pre_if` - if文前のスナップショット
/// * `then_end` - then-branch終了時の変数マップ
/// * `else_end_opt` - else-branch終了時の変数マップNoneの場合はempty else
pub fn analyze(
pre_if: &HashMap<String, ValueId>,
then_end: &HashMap<String, ValueId>,
else_end_opt: &Option<HashMap<String, ValueId>>,
) -> Self {
let mut all_vars = HashSet::new();
all_vars.extend(pre_if.keys().cloned());
all_vars.extend(then_end.keys().cloned());
if let Some(ref else_map) = else_end_opt {
all_vars.extend(else_map.keys().cloned());
}
let changed = crate::mir::phi_core::if_phi::compute_modified_names(
pre_if,
then_end,
else_end_opt,
);
let changed_vars = changed.into_iter().collect();
Self {
all_vars,
changed_vars,
}
}
/// Conservative フォールバック値取得
///
/// # Returns
/// * `Some((then_v, else_v))` - 両ブランチの値void emission 含む)
/// * `None` - どこにも定義されていない変数(スキップ)
///
/// # Conservative Rules
/// 1. Both defined: use both values
/// 2. Only then: use then + void
/// 3. Only else: use void + else
/// 4. Neither: skip (return None)
pub fn get_conservative_values<F>(
&self,
name: &str,
pre_if: &HashMap<String, ValueId>,
then_end: &HashMap<String, ValueId>,
else_end_opt: &Option<HashMap<String, ValueId>>,
emit_void: F,
) -> Option<(ValueId, ValueId)>
where
F: Fn() -> ValueId,
{
let pre_val_opt = pre_if.get(name).copied();
// Fallback to predecessor value if not defined in a branch
let then_v_opt = then_end.get(name).copied().or(pre_val_opt);
let else_v_opt = else_end_opt
.as_ref()
.and_then(|m| m.get(name).copied())
.or(pre_val_opt);
match (then_v_opt, else_v_opt) {
(Some(tv), Some(ev)) => Some((tv, ev)),
(Some(tv), None) => {
// Variable exists in then branch but not else or predecessor
// Emit a 'const void' instruction to represent undefined value
Some((tv, emit_void()))
}
(None, Some(ev)) => {
// Variable exists in else branch but not then or predecessor
// Emit a 'const void' instruction to represent undefined value
Some((emit_void(), ev))
}
(None, None) => {
// Variable doesn't exist anywhere - skip
None
}
}
}
/// Debug trace 出力Conservative PHI生成の可視化
pub fn trace_if_enabled(&self, pre_if: &HashMap<String, ValueId>, then_end: &HashMap<String, ValueId>, else_end_opt: &Option<HashMap<String, ValueId>>) {
let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE")
.ok()
.as_deref()
== Some("1");
if trace_conservative {
eprintln!("[Conservative PHI] all_vars count: {}", self.all_vars.len());
eprintln!(
"[Conservative PHI] pre_if_snapshot: {:?}",
pre_if.keys().collect::<Vec<_>>()
);
eprintln!(
"[Conservative PHI] then_map_end: {:?}",
then_end.keys().collect::<Vec<_>>()
);
if let Some(ref else_map) = else_end_opt {
eprintln!(
"[Conservative PHI] else_map_end: {:?}",
else_map.keys().collect::<Vec<_>>()
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conservative_merge_both_defined() {
let mut pre_if = HashMap::new();
pre_if.insert("x".to_string(), ValueId::new(1));
let mut then_end = HashMap::new();
then_end.insert("x".to_string(), ValueId::new(2));
let mut else_end = HashMap::new();
else_end.insert("x".to_string(), ValueId::new(3));
let merge = ConservativeMerge::analyze(&pre_if, &then_end, &Some(else_end));
assert_eq!(merge.all_vars.len(), 1);
assert!(merge.all_vars.contains("x"));
}
#[test]
fn test_conservative_merge_union() {
let pre_if = HashMap::new();
let mut then_end = HashMap::new();
then_end.insert("x".to_string(), ValueId::new(1));
let mut else_end = HashMap::new();
else_end.insert("y".to_string(), ValueId::new(2));
let merge = ConservativeMerge::analyze(&pre_if, &then_end, &Some(else_end));
assert_eq!(merge.all_vars.len(), 2);
assert!(merge.all_vars.contains("x"));
assert!(merge.all_vars.contains("y"));
}
}

View File

@ -8,6 +8,7 @@
*/
pub mod common;
pub mod conservative;
pub mod if_phi;
pub mod loop_phi;
pub mod loopform_builder;