完全移行→削除の安全順序(Option C)に従い、VariableContext の deprecated フィールドと sync helpers を完全削除。 ## Changes - Migrated all 66+ access sites to variable_ctx.variable_map - Removed 1 deprecated field (variable_map) from MirBuilder - Removed 2 sync helpers (sync_variable_ctx_to_legacy, sync_legacy_to_variable_ctx) - Fixed BoxCompilationContext.variable_map references (kept as-is, different scope) - Fixed ExitBindingBuilder.variable_map references (kept as-is, local field) - Updated observer.rs to use variable_map() accessor method ## JoinIR Integration Verified - CarrierInfo::from_variable_map() works correctly - ExitLine contract maintained (Phase 132-135) - NYASH_TRACE_VARMAP debug support preserved - Pattern 1-5 lowering all functional ## Tests - cargo test --release --lib: 1033 passed, 0 failed - phase135_trim_mir_verify.sh: PASS (MIR SSA/ValueId OK) - cargo build --release: SUCCESS - Deprecation warnings: reduced (86 remaining, from other contexts) ## Statistics - 27 files changed - 146 insertions(+), 174 deletions(-) - Net: -28 lines Phase 2 Progress: 6/7 contexts complete (86%) - ✅ MetadataContext - ✅ CoreContext - ✅ TypeContext - ✅ ScopeContext - ✅ BindingContext - ✅ VariableContext (this commit) - ⏳ CompilationContext (Phase 2-7 next) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
285 lines
11 KiB
Rust
285 lines
11 KiB
Rust
//! 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, BTreeSet, HashSet}; // Phase 25.1: 決定性確保, Phase 58: インライン化
|
||
|
||
/// 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_ctx.variable_map.insert(name, v);
|
||
Ok(())
|
||
}
|
||
_ => {
|
||
// Multiple predecessors - insert PHI
|
||
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
|
||
func.update_cfg();
|
||
}
|
||
if let (Some(func), Some(cur_bb)) =
|
||
(&self.builder.scope_ctx.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_ctx.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.scope_ctx.current_function.as_mut() {
|
||
func.update_cfg();
|
||
}
|
||
if let (Some(func), Some(cur_bb)) =
|
||
(&self.builder.scope_ctx.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(changed_vars) - Set of variables that were changed, for pin handling
|
||
///
|
||
/// # Phase 57-58 改善
|
||
///
|
||
/// - Phase 57: 戻り値を `()` から `HashSet<String>` に変更
|
||
/// - Phase 58: ConservativeMerge::analyze をインライン化
|
||
///
|
||
/// # Phase 58 改善
|
||
///
|
||
/// ConservativeMerge::analyze をインライン化。
|
||
/// conservative.rs の struct は削除され、ロジックのみここに残る。
|
||
///
|
||
/// ## Conservative ∘ Elimination = Minimal SSA
|
||
///
|
||
/// - Conservative (this): correctness-first, generate all PHIs
|
||
/// - Elimination (future): efficiency optimization, remove unused PHIs
|
||
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<HashSet<String>, String> {
|
||
// ========================================
|
||
// Phase 58: ConservativeMerge::analyze インライン化
|
||
// ========================================
|
||
// 旧: crate::mir::phi_core::conservative::ConservativeMerge::analyze(...)
|
||
// 新: 以下のロジックを直接ここに記述
|
||
|
||
// 1. all_vars: 全ブランチに存在する変数のユニオン(Conservative戦略)
|
||
let mut all_vars = HashSet::new();
|
||
all_vars.extend(pre_if_snapshot.keys().cloned());
|
||
all_vars.extend(then_map_end.keys().cloned());
|
||
if let Some(ref else_map) = else_map_end_opt {
|
||
all_vars.extend(else_map.keys().cloned());
|
||
}
|
||
|
||
// 2. changed_vars: 実際に変更された変数のセット
|
||
// 決定的順序のためBTreeSet使用
|
||
let mut names: BTreeSet<&str> = BTreeSet::new();
|
||
for k in then_map_end.keys() {
|
||
names.insert(k.as_str());
|
||
}
|
||
if let Some(emap) = else_map_end_opt.as_ref() {
|
||
for k in emap.keys() {
|
||
names.insert(k.as_str());
|
||
}
|
||
}
|
||
let mut changed_vars = HashSet::new();
|
||
// アルファベット順で決定的にイテレート
|
||
for &name in &names {
|
||
let pre = pre_if_snapshot.get(name);
|
||
let t = then_map_end.get(name);
|
||
let e = else_map_end_opt.as_ref().and_then(|m| m.get(name));
|
||
if (t.is_some() && Some(*t.unwrap()) != pre.copied())
|
||
|| (e.is_some() && Some(*e.unwrap()) != pre.copied())
|
||
{
|
||
changed_vars.insert(name.to_string());
|
||
}
|
||
}
|
||
// ========================================
|
||
|
||
let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE")
|
||
.ok()
|
||
.as_deref()
|
||
== Some("1");
|
||
|
||
for name in &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)?;
|
||
}
|
||
|
||
// Phase 57: 変更された変数セットを返す(phi.rsでの冗長呼び出し削除用)
|
||
Ok(changed_vars)
|
||
}
|
||
}
|