Files
hakorune/src/mir/builder/if_form.rs
nyash-codex cf4b615afb mir/vm: SSA pin+PHI + short-circuit; user-defined method calls → functions; entry single-pred PHIs; compare-operand pin; VM BoxCall fallback to InstanceBox methods; docs: update CURRENT_TASK (plan + acceptance)
- Lower And/Or to branch+PHI (RHS not evaluated)
- Always slotify compare operands (dominance safety)
- Insert single-predecessor PHIs at then/else/short-circuit entries
- pin_to_slot now logs (NYASH_PIN_TRACE) and participates in PHI
- Rewrite user-defined instance method calls to Box.method/Arity (builder)
- VM fallback: BoxCall on InstanceBox dispatches to lowered functions with 'me'+args
- Keep plugin/BoxCall path for core boxes (String/Array/Map)
- Add env-gated pre-pin for if/loop (NYASH_MIR_PREPIN)
- CURRENT_TASK: add SSA/userbox plan, debug steps, acceptance criteria
2025-09-26 05:28:20 +09:00

167 lines
6.9 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::{ConstValue, MirBuilder, MirInstruction, ValueId};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::ast::{ASTNode, BinaryOperator};
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> {
// Heuristic pre-pin: if condition is a comparison, evaluate its operands now and pin them
// so that subsequent branches can safely reuse these values across blocks.
// This leverages existing variable_map merges (PHI) at the merge block.
if crate::config::env::mir_pre_pin_compare_operands() {
if let ASTNode::BinaryOp { operator, left, right, .. } = &condition {
match operator {
BinaryOperator::Equal
| BinaryOperator::NotEqual
| BinaryOperator::Less
| BinaryOperator::LessEqual
| BinaryOperator::Greater
| BinaryOperator::GreaterEqual => {
if let Ok(lhs_v) = self.build_expression((**left).clone()) {
let _ = self.pin_to_slot(lhs_v, "@if_lhs");
}
if let Ok(rhs_v) = self.build_expression((**right).clone()) {
let _ = self.pin_to_slot(rhs_v, "@if_rhs");
}
}
_ => {}
}
}
}
let condition_val = self.build_expression(condition)?;
// Create blocks
let then_block = self.block_gen.next();
let else_block = self.block_gen.next();
let merge_block = self.block_gen.next();
// Branch
let pre_branch_bb = self.current_block()?;
self.emit_instruction(MirInstruction::Branch {
condition: condition_val,
then_bb: then_block,
else_bb: else_block,
})?;
// Snapshot variables before entering branches
let pre_if_var_map = self.variable_map.clone();
// then
self.start_new_block(then_block)?;
// Scope enter for then-branch
self.hint_scope_enter(0);
let then_ast_for_analysis = then_branch.clone();
self.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.value_gen.next();
let inputs = vec![(pre_branch_bb, pre_v)];
self.emit_instruction(MirInstruction::Phi { dst: phi_val, inputs })?;
self.variable_map.insert(name.clone(), phi_val);
}
let then_value_raw = self.build_expression(then_branch)?;
let then_exit_block = self.current_block()?;
let then_var_map_end = self.variable_map.clone();
if !self.is_current_block_terminated() {
// Scope leave for then-branch
self.hint_scope_leave(0);
self.emit_instruction(MirInstruction::Jump { target: merge_block })?;
}
// else
self.start_new_block(else_block)?;
// Scope enter for else-branch
self.hint_scope_enter(0);
// 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.value_gen.next();
let inputs = vec![(pre_branch_bb, pre_v)];
self.emit_instruction(MirInstruction::Phi { dst: phi_val, inputs })?;
self.variable_map.insert(name.clone(), phi_val);
}
let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) = if let Some(else_ast) = else_branch {
self.variable_map = pre_if_var_map.clone();
let val = self.build_expression(else_ast.clone())?;
(val, Some(else_ast), Some(self.variable_map.clone()))
} else {
let void_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?;
(void_val, None, None)
};
let else_exit_block = self.current_block()?;
if !self.is_current_block_terminated() {
// Scope leave for else-branch
self.hint_scope_leave(0);
self.emit_instruction(MirInstruction::Jump { target: 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)?;
self.push_if_merge(merge_block);
// Pre-analysis: identify then/else assigned var for skip and hints
let assigned_then_pre = crate::mir::phi_core::if_phi::extract_assigned_var(&then_ast_for_analysis);
let assigned_else_pre = else_ast_for_analysis
.as_ref()
.and_then(|e| crate::mir::phi_core::if_phi::extract_assigned_var(e));
let pre_then_var_value = assigned_then_pre
.as_ref()
.and_then(|name| pre_if_var_map.get(name).copied());
let result_val = self.normalize_if_else_phi(
then_block,
else_block,
Some(then_exit_block),
Some(else_exit_block),
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)
let skip_name = assigned_then_pre.as_deref();
self.merge_modified_vars(
then_block,
else_block,
then_exit_block,
Some(else_exit_block),
&pre_if_var_map,
&then_var_map_end,
&else_var_map_end_opt,
skip_name,
)?;
self.pop_if_merge();
Ok(result_val)
}
}