Files
hakorune/src/mir/builder/phi_merge.rs
nyash-codex d3eff1fceb feat(joinir): Phase 45-46 read_quoted_from IfMerge implementation
Phase 45: read_quoted_from JoinIR Frontend/Bridge
- Implement lower_read_quoted_pattern() for Guard if + Loop with break + accumulator pattern
- Add T1-T4 Route B E2E tests (all PASS)
- Create phase45_read_quoted_fixture.hako for Route A testing

Phase 46: IfMerge extension for loop-internal if-body reassignment
- Add escape handling: if ch == "\\" { i = i+1; ch = s.substring(...) }
- Use IfMerge to merge i and ch after if-body (speculative execution)
- T5 PASS: "a\"b" → 'a"b' (escape handling works!)

Dev flags:
- HAKO_JOINIR_READ_QUOTED=1: Enable Phase 45 JoinIR route
- HAKO_JOINIR_READ_QUOTED_IFMERGE=1: Enable Phase 46 IfMerge escape handling

Test results (Route B):
- T1: "abc" → 'abc' 
- T2: "" → '' 
- T3: abc → '' 
- T4: xx"def" → 'def' 
- T5: "a\"b" → 'a"b' 

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 17:13:52 +09:00

238 lines
8.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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(())
}
}