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

415 lines
16 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.

use super::{MirBuilder, ValueId};
use crate::ast::ASTNode;
use crate::mir::control_form::IfShape;
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::phi_core::phi_builder_box::PhiBuilderOps;
use crate::mir::{BasicBlockId, ConstValue, MirInstruction};
/// Phase 61-4-F: MirBuilder 用 PhiBuilderOps 実装
///
/// ループ外 if の JoinIR 経路で emit_toplevel_phis() を呼ぶためのラッパー。
struct ToplevelOps<'a>(&'a mut MirBuilder);
impl<'a> PhiBuilderOps for ToplevelOps<'a> {
fn new_value(&mut self) -> ValueId {
self.0.next_value_id()
}
fn emit_phi(
&mut self,
block: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
// merge ブロックの先頭に PHI を挿入
if let (Some(func), Some(_cur_bb)) = (
self.0.scope_ctx.current_function.as_mut(),
self.0.current_block,
) {
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
block,
dst,
inputs,
self.0.metadata_ctx.current_span(),
);
} else {
self.0.emit_instruction(MirInstruction::Phi {
dst,
inputs,
type_hint: None,
})?;
}
Ok(())
}
fn update_var(&mut self, name: String, value: ValueId) {
self.0.variable_ctx.variable_map.insert(name, value);
}
fn get_block_predecessors(&self, block: BasicBlockId) -> Vec<BasicBlockId> {
if let Some(ref func) = self.0.scope_ctx.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.next_value_id();
let _ = self.0.emit_instruction(MirInstruction::Const {
dst: void_id,
value: ConstValue::Void,
});
void_id
}
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
self.0.current_block = Some(block);
Ok(())
}
fn block_exists(&self, block: BasicBlockId) -> bool {
if let Some(ref func) = self.0.scope_ctx.current_function {
func.blocks.contains_key(&block)
} else {
false
}
}
}
impl MirBuilder {
/// Lower an if/else using a structured IfForm (header→then/else→merge).
/// PHI-off: edge-copy only on predecessors; PHI-on: Phi at merge.
pub(super) fn lower_if_form(
&mut self,
condition: ASTNode,
then_branch: ASTNode,
else_branch: Option<ASTNode>,
) -> Result<ValueId, String> {
// Reserve a deterministic join id for debug region labeling
let join_id = self.debug_next_join_id();
// Pre-pin heuristic was deprecated; keep operands as-is for predictability.
let condition_val = self.build_expression(condition)?;
let condition_val = self.local_cond(condition_val);
// Create blocks
let then_block = self.next_block_id();
let else_block = self.next_block_id();
let merge_block = self.next_block_id();
// Branch
let pre_branch_bb = self.current_block()?;
let mut condition_val = condition_val;
crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut condition_val);
// Phase 268 P0: emit_conditional() deleted (replaced by emit_conditional_edgecfg() at line 206)
// Snapshot variables before entering branches
let pre_if_var_map = self.variable_ctx.variable_map.clone();
let trace_if = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");
// then
self.start_new_block(then_block)?;
// Debug region: join then-branch
self.debug_push_region(format!("join#{}", join_id) + "/then");
// Scope enter for then-branch
self.hint_scope_enter(0);
let then_ast_for_analysis = then_branch.clone();
self.variable_ctx.variable_map = pre_if_var_map.clone();
// Materialize all variables at block entry via single-pred Phi (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_ctx.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] then-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
}
}
let then_value_raw = self.build_expression(then_branch)?;
let then_exit_block = self.current_block()?;
let then_reaches_merge = !self.is_current_block_terminated();
let then_var_map_end = self.variable_ctx.variable_map.clone();
if then_reaches_merge {
// Scope leave for then-branch
self.hint_scope_leave(0);
// Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
}
// Pop then-branch debug region
self.debug_pop_region();
// else
self.start_new_block(else_block)?;
// Debug region: join else-branch
self.debug_push_region(format!("join#{}", join_id) + "/else");
// Scope enter for else-branch
self.hint_scope_enter(0);
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) =
if let Some(else_ast) = else_branch {
// Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
self.variable_ctx.variable_map = pre_if_var_map.clone();
// Materialize all variables at block entry via single-pred Phi (correctness-first)
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_ctx.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
}
}
let val = self.build_expression(else_ast.clone())?;
(
val,
Some(else_ast),
Some(self.variable_ctx.variable_map.clone()),
)
} else {
// No else branch: materialize PHI nodes for the empty else block
self.variable_ctx.variable_map = pre_if_var_map.clone();
for (name, &pre_v) in pre_if_var_map.iter() {
let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
self.variable_ctx.variable_map.insert(name.clone(), phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name, pre_v, phi_val
);
}
}
let void_val = crate::mir::builder::emission::constant::emit_void(self);
// Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
// This ensures merge_modified_vars uses correct ValueIds after PHI renaming
(void_val, None, Some(self.variable_ctx.variable_map.clone()))
};
let else_exit_block = self.current_block()?;
let else_reaches_merge = !self.is_current_block_terminated();
if else_reaches_merge {
// Scope leave for else-branch
self.hint_scope_leave(0);
// Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
}
// Pop else-branch debug region
self.debug_pop_region();
// Phase 268 P0: EdgeCFG Fragment ベース emitemission 層経由)
crate::mir::builder::emission::branch::emit_conditional_edgecfg(
self,
pre_branch_bb,
condition_val,
then_block,
then_exit_block,
then_reaches_merge,
else_block,
else_exit_block,
else_reaches_merge,
merge_block,
)?;
// merge: primary result via helper, then delta-based variable merges
// Ensure PHIs are first in the block by suppressing entry pin copies here
self.suppress_next_entry_pin_copy();
self.start_new_block(merge_block)?;
// Debug region: join merge
self.debug_push_region(format!("join#{}", join_id) + "/join");
self.push_if_merge(merge_block);
// Phase 38: Pre-analysis hints removed (JoinIR AST lowering handles assignment detection)
let assigned_then_pre: Option<String> = None;
let assigned_else_pre: Option<String> = None;
let pre_then_var_value: Option<ValueId> = None;
// Phase 61-4: JoinIR 経路試行(ループ外 If
//
// - 実際の有効化判定は config::env 側のポリシー関数に集約する。
let joinir_enabled = crate::config::env::joinir_if_select_enabled();
let joinir_toplevel = crate::config::env::joinir_if_toplevel_enabled();
let joinir_dryrun = crate::config::env::joinir_if_toplevel_dryrun_enabled();
let mut joinir_success = false;
// 関数名ガードチェック
let func_name = self
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("");
let is_target = crate::mir::join_ir::lowering::is_joinir_if_toplevel_target(func_name);
// Phase 80: Core ON 時は代表関数で JoinIR を本線として試行
let core_mainline =
crate::mir::join_ir::lowering::should_try_joinir_mainline(func_name, false);
let strict_mode =
crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false);
// Core ON + 本線対象の場合は環境変数に関わらず試行
let should_try_joinir =
core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun));
if should_try_joinir {
if let Some(ref func) = self.scope_ctx.current_function {
let context =
crate::mir::join_ir::lowering::if_phi_context::IfPhiContext::pure_if();
match crate::mir::join_ir::lowering::try_lower_if_to_joinir(
func,
pre_branch_bb,
false,
Some(&context),
) {
Some(join_inst) => {
if joinir_dryrun || joinir_toplevel {
eprintln!(
"[Phase 61-4] ✅ Toplevel If lowered via JoinIR ({}): {:?}",
func_name, join_inst
);
}
// PhiSpec 計算
let phi_spec = crate::mir::join_ir::lowering::if_phi_spec::compute_phi_spec_from_joinir(&context, &join_inst);
if joinir_dryrun {
eprintln!(
"[Phase 61-4] 🔍 dry-run: JoinIR PhiSpec header={}, exit={}",
phi_spec.header_count(),
phi_spec.exit_count()
);
}
// Phase 61-4-F: 本番経路 - emit_toplevel_phis でPHI生成
if joinir_toplevel {
// IfShape 構築
let if_shape = IfShape {
cond_block: pre_branch_bb,
then_block,
else_block: Some(else_block),
merge_block,
};
// PHI 生成
let phi_count = {
let mut ops = ToplevelOps(self);
crate::mir::if_in_loop_phi::IfInLoopPhiEmitter::emit_toplevel_phis(
&phi_spec,
&pre_if_var_map,
&then_var_map_end,
else_var_map_end_opt.as_ref(),
&mut ops,
&if_shape,
)?
};
if joinir_dryrun {
eprintln!(
"[Phase 61-4] ✅ Production path: {} PHIs generated via JoinIR",
phi_count
);
}
joinir_success = true;
}
}
None => {
if joinir_dryrun {
eprintln!(
"[Phase 61-4] ⏭️ JoinIR pattern not matched for {}, using fallback",
func_name
);
}
// Phase 80/81: Strict mode では本線対象関数の失敗でパニック
if strict_mode {
panic!(
"[joinir/if] strict mode: pattern not matched for {} (if_form.rs)",
func_name
);
}
}
}
}
}
// Phase 61-4: JoinIR成功時はスキップ、失敗時は既存経路
let result_val = if joinir_success {
// JoinIR 経路(未実装 - フォールバック)
crate::mir::builder::emission::constant::emit_void(self)
} else {
self.normalize_if_else_phi(
then_block,
else_block,
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
then_value_raw,
else_value_raw,
&pre_if_var_map,
&then_ast_for_analysis,
&else_ast_for_analysis,
&then_var_map_end,
&else_var_map_end_opt,
pre_then_var_value,
)?
};
// Hint: join result variable(s)
// 1) Primary: if both branches assign to the same variable name, emit a hint for that name
if let (Some(tn), Some(en)) = (assigned_then_pre.as_deref(), assigned_else_pre.as_deref()) {
if tn == en {
self.hint_join_result(tn);
}
}
// 2) Secondary: if both branches assign multiple variables, hint全件制限なし
if let Some(ref else_map_end) = else_var_map_end_opt {
for name in then_var_map_end.keys() {
if Some(name.as_str()) == assigned_then_pre.as_deref() {
continue;
}
if else_map_end.contains_key(name) {
self.hint_join_result(name.as_str());
}
}
}
// Merge other modified variables (skip the primary assignment if any)
if !joinir_success {
let skip_name = assigned_then_pre.as_deref();
self.merge_modified_vars(
then_block,
else_block,
if then_reaches_merge {
Some(then_exit_block)
} else {
None
},
if else_reaches_merge {
Some(else_exit_block)
} else {
None
},
&pre_if_var_map,
&then_var_map_end,
&else_var_map_end_opt,
skip_name,
)?;
}
self.pop_if_merge();
// Pop merge debug region
self.debug_pop_region();
Ok(result_val)
}
}