Files
hakorune/src/mir/builder/phi_merge.rs

238 lines
8.4 KiB
Rust
Raw Normal View History

//! PHI Merge Helper - Unified PHI insertion logic
//!
//! Phase 25.1q: PhiMergeHelper統一化
//! - phi.rs の2箇所のPHI insertion重複L120-137 vs L155-174を統一
//! - Conservative戦略によるvariable merging の一元化
//!
//! Box-First理論: PHI insertion の境界を明確にし、差し替え可能な箱として提供
use super::{BasicBlockId, MirBuilder, ValueId};
use std::collections::BTreeMap; // Phase 25.1: 決定性確保
/// PHI Merge Helper - 統一PHI挿入ロジックConservative戦略
///
/// # Purpose
/// - 複数のブランチ出口からマージブロックへのPHI挿入を統一処理
/// - Conservative戦略: 全変数に対してPHIを生成correctness-first
///
/// # Usage
/// ```ignore
/// let mut helper = PhiMergeHelper::new(&mut builder, then_exit, else_exit);
/// helper.merge_variable("x".to_string(), then_v, else_v)?;
/// ```
pub struct PhiMergeHelper<'a> {
builder: &'a mut MirBuilder,
then_exit: Option<BasicBlockId>,
else_exit: Option<BasicBlockId>,
}
impl<'a> PhiMergeHelper<'a> {
/// Create a new PhiMergeHelper
///
/// # Arguments
/// * `builder` - MirBuilder instance
/// * `then_exit` - Then-branch exit block (None if unreachable)
/// * `else_exit` - Else-branch exit block (None if unreachable)
pub fn new(
builder: &'a mut MirBuilder,
then_exit: Option<BasicBlockId>,
else_exit: Option<BasicBlockId>,
) -> Self {
Self {
builder,
then_exit,
else_exit,
}
}
/// Merge a single variable using Conservative PHI strategy
///
/// # Arguments
/// * `name` - Variable name
/// * `then_v` - Value from then-branch
/// * `else_v` - Value from else-branch
///
/// # Returns
/// Ok(()) on success, Err(String) on failure
///
/// # Conservative Strategy
/// - 0 predecessors: skip (unreachable)
/// - 1 predecessor: direct insert (no PHI needed)
/// - 2+ predecessors: insert PHI node
pub fn merge_variable(
&mut self,
name: String,
then_v: ValueId,
else_v: ValueId,
) -> Result<(), String> {
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = self.then_exit {
inputs.push((tp, then_v));
}
if let Some(ep) = self.else_exit {
inputs.push((ep, else_v));
}
match inputs.len() {
0 => {
// Both branches unreachable - skip
Ok(())
}
1 => {
// Single predecessor - direct insert (no PHI)
let (_pred, v) = inputs[0];
self.builder.variable_map.insert(name, v);
Ok(())
}
_ => {
// Multiple predecessors - insert PHI
if let Some(func) = self.builder.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.builder.current_function, self.builder.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
}
let merged = self.builder.insert_phi(inputs)?;
self.builder.variable_map.insert(name, merged);
Ok(())
}
}
}
/// Merge a variable with explicit destination ValueId (for primary result)
///
/// # Arguments
/// * `dst` - Destination ValueId for PHI result
/// * `then_v` - Value from then-branch
/// * `else_v` - Value from else-branch
///
/// # Returns
/// Ok(()) on success, Err(String) on failure
#[allow(dead_code)] // Reserved: explicit dst PHI merge for future use
pub fn merge_with_dst(
&mut self,
dst: ValueId,
then_v: ValueId,
else_v: ValueId,
) -> Result<(), String> {
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
if let Some(tp) = self.then_exit {
inputs.push((tp, then_v));
}
if let Some(ep) = self.else_exit {
inputs.push((ep, else_v));
}
match inputs.len() {
0 | 1 => {
// Should not happen for explicit dst merge
Ok(())
}
_ => {
// Insert PHI with explicit dst
if let Some(func) = self.builder.current_function.as_mut() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.builder.current_function, self.builder.current_block)
{
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
}
self.builder.insert_phi_with_dst(dst, inputs)?;
Ok(())
}
}
}
/// Merge all variables from both branches (Conservative strategy)
///
/// # Arguments
/// * `pre_if_snapshot` - Variable map before if statement
/// * `then_map_end` - Variable map at end of then-branch
/// * `else_map_end_opt` - Variable map at end of else-branch (None for empty else)
/// * `skip_var` - Optional variable name to skip (already merged elsewhere)
///
/// # Returns
/// Ok(()) on success, Err(String) on failure
pub fn merge_all_vars(
&mut self,
pre_if_snapshot: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
then_map_end: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
else_map_end_opt: &Option<BTreeMap<String, ValueId>>, // Phase 25.1: BTreeMap化
skip_var: Option<&str>,
) -> Result<(), String> {
// Use Conservative strategy from conservative module
let conservative = crate::mir::phi_core::conservative::ConservativeMerge::analyze(
pre_if_snapshot,
then_map_end,
else_map_end_opt,
);
// Phase 42: trace_if_enabled 削除(下の trace_conservative と重複していたため)
let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE")
.ok()
.as_deref()
== Some("1");
for name in &conservative.all_vars {
if skip_var.map(|s| s == name.as_str()).unwrap_or(false) {
if trace_conservative {
eprintln!("[Conservative PHI] Skipping {}: matches skip_var", name);
}
continue;
}
let pre_val_opt = pre_if_snapshot.get(name.as_str()).copied();
let then_v_opt = then_map_end.get(name.as_str()).copied().or(pre_val_opt);
let else_v_opt = else_map_end_opt
.as_ref()
.and_then(|m| m.get(name.as_str()).copied())
.or(pre_val_opt);
let (then_v, else_v) = match (then_v_opt, else_v_opt) {
(Some(tv), Some(ev)) => {
if trace_conservative {
eprintln!(
"[Conservative PHI] Generating PHI for {}: then={:?} else={:?}",
name, tv, ev
);
}
(tv, ev)
}
(Some(tv), None) => {
let undef = crate::mir::builder::emission::constant::emit_void(self.builder);
if trace_conservative {
eprintln!(
"[Conservative PHI] One-branch variable {}: then={:?} else=void({:?})",
name, tv, undef
);
}
(tv, undef)
}
(None, Some(ev)) => {
let undef = crate::mir::builder::emission::constant::emit_void(self.builder);
if trace_conservative {
eprintln!(
"[Conservative PHI] One-branch variable {}: then=void({:?}) else={:?}",
name, undef, ev
);
}
(undef, ev)
}
(None, None) => {
if trace_conservative {
eprintln!("[Conservative PHI] Skipping {}: undefined everywhere", name);
}
continue;
}
};
self.merge_variable(name.clone(), then_v, else_v)?;
}
Ok(())
}
}