Files
hakorune/src/mir/loop_builder.rs
nyash-codex 75f3df2505 refactor(mir): Phase 25.1o - do_break/continue 共通化(LoopExitKind型統一)
【変更内容】
1. LoopExitKind enum定義
   - Break / Continue の型安全な区別

2. do_loop_exit() 共通メソッド作成(47行)
   - スナップショット取得(共通処理)
   - kind別のスナップショット保存
   - kind別のジャンプターゲット
   - unreachable ブロック切り替え(共通処理)

3. do_break/continue をthin wrapperに変換
   - do_break: 13行 → 4行
   - do_continue: 12行 → 4行
   - 合計21行削減

【効果】
- 構造改善: break/continue の共通ロジック一箇所に集約
- 保守性向上: デバッグログなどの共通処理が統一管理
- 拡張性向上: labeled break/continue等の将来拡張が容易

【検証結果】
- ビルド成功(警告なし)
- mir_stageb_loop_break_continue_verifies: PASS
- /tmp/loop_continue_fixed.hako: RC=3(期待通り)

関連: Phase 25.1m (continue PHI修正), Phase 25.1n (レガシー削除)
2025-11-19 08:56:44 +09:00

1041 lines
44 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.

/*!
* MIR Loop Builder - SSA形式でのループ構築専用モジュール
*
* Sealed/Unsealed blockとPhi nodeを使った正しいループ実装
* Based on Gemini's recommendation for proper SSA loop handling
*/
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
use crate::mir::control_form::{ControlForm, IfShape, LoopShape, is_control_form_trace_on};
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::ast::ASTNode;
use std::collections::HashMap;
// Phase 15 段階的根治戦略:制御フローユーティリティ
use super::utils::{
is_current_block_terminated,
capture_actual_predecessor_and_jump,
};
/// ループ脱出の種類(箱化・共通化のための型)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LoopExitKind {
/// break文exit blockへジャンプ
Break,
/// continue文header blockへジャンプ
Continue,
}
/// ループビルダー - SSA形式でのループ構築を管理
pub struct LoopBuilder<'a> {
/// 親のMIRビルダーへの参照
parent_builder: &'a mut super::builder::MirBuilder,
/// ブロックごとの変数マップ(スコープ管理)
#[allow(dead_code)]
block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>>,
/// ループヘッダーIDcontinueで使用
loop_header: Option<BasicBlockId>,
/// continue文からの変数スナップショット
continue_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
/// break文からの変数スナップショットexit PHI生成用
exit_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
// フェーズM: no_phi_modeフィールド削除常にPHI使用
}
impl<'a> LoopBuilder<'a> {
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
/// Find the source value of a Copy instruction in a given block
/// If `dst` is defined by a Copy instruction `dst = copy src`, return Some(src)
/// Otherwise return None
// =============================================================
// Control Helpers — break/continue/jumps/unreachable handling
// =============================================================
/// Emit a jump to `target` from the current block and record predecessor metadata.
fn jump_with_pred(&mut self, target: BasicBlockId) -> Result<(), String> {
let cur_block = self.current_block()?;
self.emit_jump(target)?;
let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, target, cur_block);
Ok(())
}
/// Switch insertion to a fresh (unreachable) block and place a Void const to keep callers satisfied.
fn switch_to_unreachable_block_with_void(&mut self) -> Result<ValueId, String> {
let next_block = self.new_block();
self.set_current_block(next_block)?;
let void_id = self.new_value();
self.emit_const(void_id, ConstValue::Void)?;
Ok(void_id)
}
/// 【箱化】ループ脱出の共通処理break/continue統一化
///
/// Phase 25.1o: break と continue の共通パターンを抽出し、
/// LoopExitKind で振る舞いを切り替える統一メソッド。
///
/// # 処理フロー
/// 1. 現在の変数マップをスナップショット
/// 2. 適切なスナップショットリストに追加Break → exit_snapshots, Continue → continue_snapshots
/// 3. ターゲットブロックへジャンプBreak → exit, Continue → header
/// 4. unreachable ブロックに切り替え
fn do_loop_exit(&mut self, kind: LoopExitKind) -> Result<ValueId, String> {
// 1. スナップショット取得(共通処理)
let snapshot = self.get_current_variable_map();
let cur_block = self.current_block()?;
// 2. スナップショット保存kind別処理
match kind {
LoopExitKind::Break => {
if std::env::var("NYASH_LOOPFORM_DEBUG").ok().as_deref() == Some("1") {
eprintln!("[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
cur_block, snapshot.keys().collect::<Vec<_>>());
}
self.exit_snapshots.push((cur_block, snapshot));
}
LoopExitKind::Continue => {
self.block_var_maps.insert(cur_block, snapshot.clone());
self.continue_snapshots.push((cur_block, snapshot));
}
}
// 3. ターゲットブロックへジャンプkind別処理
match kind {
LoopExitKind::Break => {
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) {
self.jump_with_pred(exit_bb)?;
}
}
LoopExitKind::Continue => {
if let Some(header) = self.loop_header {
self.jump_with_pred(header)?;
}
}
}
// 4. unreachable ブロックに切り替え(共通処理)
self.switch_to_unreachable_block_with_void()
}
/// Handle a `break` statement: jump to loop exit and continue in a fresh unreachable block.
/// 【箱化】do_loop_exit() への thin wrapper
fn do_break(&mut self) -> Result<ValueId, String> {
self.do_loop_exit(LoopExitKind::Break)
}
/// Handle a `continue` statement: snapshot vars, jump to loop header, then continue in a fresh unreachable block.
/// 【箱化】do_loop_exit() への thin wrapper
fn do_continue(&mut self) -> Result<ValueId, String> {
self.do_loop_exit(LoopExitKind::Continue)
}
// =============================================================
// Lifecycle — create builder, main loop construction
// =============================================================
/// 新しいループビルダーを作成
pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self {
Self {
parent_builder: parent,
block_var_maps: HashMap::new(),
loop_header: None,
continue_snapshots: Vec::new(),
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
}
}
/// SSA形式でループを構築 (LoopForm v2 only)
pub fn build_loop(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
// Phase 7-F: Legacy loop builder removed - LoopForm v2 is now the only implementation
self.build_loop_with_loopform(condition, body)
}
/// SSA形式でループを構築 (LoopFormBuilder implementation)
fn build_loop_with_loopform(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_loop_with_loopform] === ENTRY ===");
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
}
eprintln!("[build_loop_with_loopform] condition={:?}", condition);
eprintln!("[build_loop_with_loopform] body.len()={}", body.len());
}
// Create loop structure blocks following LLVM canonical form
// We need a dedicated preheader block to materialize loop entry copies
let before_loop_id = self.current_block()?;
// Capture variable snapshot BEFORE creating new blocks (at loop entry point)
let current_vars = self.get_current_variable_map();
// DEBUG: Show variable map before guard check
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform] before_loop_id={:?}, variable_map size={}",
before_loop_id, current_vars.len());
for (name, value) in &current_vars {
eprintln!(" {} -> {:?}", name, value);
}
}
// GUARD: Check for invalid ValueId(0) before proceeding
// ValueId(0) indicates uninitialized variables - skip loop construction entirely
for (name, value) in &current_vars {
if value.0 == 0 {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_loop_with_loopform] ⚠️ GUARD: Detected ValueId(0) for '{}', skipping entire loop construction", name);
eprintln!("[build_loop_with_loopform] Returning ValueId(0) without emitting any instructions");
}
// Return ValueId(0) directly without emitting instructions
// This allows the caller to retry loop construction with properly initialized variables
return Ok(ValueId(0));
}
}
let preheader_id = self.new_block();
let header_id = self.new_block();
let body_id = self.new_block();
let latch_id = self.new_block();
let exit_id = self.new_block();
// Jump from current block to preheader
let entry_block = self.current_block()?;
self.emit_jump(preheader_id)?;
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, preheader_id, entry_block)?;
// Initialize LoopFormBuilder with preheader and header blocks
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
// Pass 1: Prepare structure (allocate all ValueIds upfront)
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}",
preheader_id, header_id, body_id, latch_id, exit_id);
eprintln!("[loopform] variable_map at loop entry (size={}):", current_vars.len());
let mut loop_count = 0;
for (name, value) in &current_vars {
loop_count += 1;
eprintln!(" [{}] {} -> {:?}", loop_count, name, value);
let is_param = self.is_parameter(name);
eprintln!(" param={}", is_param);
}
eprintln!("[loopform] iterated {} times", loop_count);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
} else {
eprintln!("[loopform] BEFORE prepare_structure: current_function=None");
}
}
loopform.prepare_structure(self, &current_vars)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
} else {
eprintln!("[loopform] AFTER prepare_structure: current_function=None");
}
}
// Pass 2: Emit preheader (copies and jump to header)
loopform.emit_preheader(self)?;
// 📦 Hotfix 6: Add CFG predecessor for header from preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id)?;
// Pass 3: Emit header PHIs (incomplete, only preheader edge)
self.set_current_block(header_id)?;
// Ensure header block exists before emitting PHIs
self.parent_builder.ensure_block_exists(header_id)?;
loopform.emit_header_phis(self)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform] variable_map after emit_header_phis:");
for (name, value) in self.get_current_variable_map().iter() {
eprintln!(" {} -> {:?}", name, value);
}
}
// Set up loop context for break/continue
crate::mir::builder::loops::push_loop_context(self.parent_builder, header_id, exit_id);
self.loop_header = Some(header_id);
self.continue_snapshots.clear();
self.exit_snapshots.clear();
// Emit condition check in header
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] BEFORE build_expression: current_block={:?}", self.current_block()?);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
}
}
let cond_value = self.parent_builder.build_expression(condition)?;
// Capture the ACTUAL block that emits the branch (might differ from header_id
// if build_expression created new blocks)
let branch_source_block = self.current_block()?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] AFTER build_expression: branch_source_block={:?}", branch_source_block);
if let Some(ref func) = self.parent_builder.current_function {
eprintln!("[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _);
}
}
self.emit_branch(cond_value, body_id, exit_id)?;
// 📦 Hotfix 6: Add CFG predecessors for branch targets (Cytron et al. 1991 requirement)
// This ensures exit_block.predecessors is populated before Exit PHI generation
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}", exit_id, branch_source_block);
}
crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, branch_source_block)?;
crate::mir::builder::loops::add_predecessor(self.parent_builder, exit_id, branch_source_block)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?);
eprintln!("[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
body_id, exit_id, branch_source_block);
// Verify predecessors were added
if let Some(ref func) = self.parent_builder.current_function {
if let Some(exit_block) = func.blocks.get(&exit_id) {
eprintln!("[loopform/condition] exit_block.predecessors = {:?}", exit_block.predecessors);
}
}
}
// Lower loop body
self.set_current_block(body_id)?;
for stmt in body {
self.build_statement(stmt)?;
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
// Capture variable snapshot at end of body (before jumping to latch)
let body_end_vars = self.get_current_variable_map();
// Jump to latch if not already terminated
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
self.emit_jump(latch_id)?;
latch_id
} else {
// Body is terminated (break/continue), use current block as latch
self.current_block()?
};
// Latch: jump back to header
self.set_current_block(latch_id)?;
// Update variable map with body end values for sealing
for (name, value) in body_end_vars {
self.update_variable(name, value);
}
self.emit_jump(header_id)?;
// 📦 Hotfix 6: Add CFG predecessor for header from latch (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, latch_id)?;
// Pass 4: Seal PHIs with latch + continue values
let continue_snaps = self.continue_snapshots.clone();
loopform.seal_phis(self, actual_latch_id, &continue_snaps)?;
// Exit block
self.set_current_block(exit_id)?;
// Phase 25.1h: ControlForm統合版に切り替え
// continue / break のターゲットブロックをユニーク化して収集
use std::collections::HashSet;
let mut cont_set: HashSet<BasicBlockId> = HashSet::new();
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
for (bb, _) in &self.continue_snapshots {
cont_set.insert(*bb);
}
for (bb, _) in &self.exit_snapshots {
break_set.insert(*bb);
}
let continue_targets: Vec<BasicBlockId> = cont_set.into_iter().collect();
let break_targets: Vec<BasicBlockId> = break_set.into_iter().collect();
let loop_shape = LoopShape {
preheader: preheader_id,
header: header_id,
body: body_id,
latch: latch_id,
exit: exit_id,
continue_targets,
break_targets,
};
let form = ControlForm::from_loop(loop_shape.clone());
// Region/GC 観測レイヤPhase 25.1l:
// NYASH_REGION_TRACE=1 のときだけ、StageB 周辺ループの
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
// Build exit PHIs for break statements using ControlForm wrapper
let exit_snaps = self.exit_snapshots.clone();
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
&loopform,
self,
&form,
&exit_snaps,
branch_source_block,
)?;
// Pop loop context
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
// ControlForm 観測: 環境フラグ未設定時は既定ONのとき LoopShape をダンプ
if is_control_form_trace_on() {
form.debug_dump();
#[cfg(debug_assertions)]
if let Some(ref func) = self.parent_builder.current_function {
loop_shape.debug_validate(func);
}
}
// Return void value
let void_dst = self.new_value();
self.emit_const(void_dst, ConstValue::Void)?;
Ok(void_dst)
}
// =============================================================
// PHI Helpers — prepare/finalize PHIs and block sealing
// =============================================================
/// ループ変数の準備(事前検出または遅延生成)
///
/// ポリシー:
/// - ループキャリア(ループ本体で再代入される変数)と pinned 変数のみを PHI 対象とする。
/// - ループ不変のローカルtext_len / pattern_len など)は preheader 値をそのまま使い、
/// 不要な PHI を張らないことで SSA 破綻(同一 ValueId の二重定義)を防ぐ。
/// ブロックをシールし、不完全なPhi nodeを完成させる
/// Exitブロックで変数のPHIを生成breakポイントでの値を統一
// --- ヘルパーメソッド(親ビルダーへの委譲) ---
fn current_block(&self) -> Result<BasicBlockId, String> {
self.parent_builder
.current_block
.ok_or_else(|| "No current block".to_string())
}
fn new_block(&mut self) -> BasicBlockId {
self.parent_builder.block_gen.next()
}
fn new_value(&mut self) -> ValueId {
// Use function-local allocator via MirBuilder helper to keep
// ValueId ranges consistent within the current function.
self.parent_builder.next_value_id()
}
fn set_current_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
self.parent_builder.start_new_block(block_id)
}
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
self.parent_builder
.emit_instruction(MirInstruction::Jump { target })
}
fn emit_branch(
&mut self,
condition: ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<(), String> {
// LocalSSA: ensure condition is materialized in the current block
let condition_local = self.parent_builder.local_ssa_ensure(condition, 4);
self.parent_builder
.emit_instruction(MirInstruction::Branch {
condition: condition_local,
then_bb,
else_bb,
})
}
fn emit_const(&mut self, dst: ValueId, value: ConstValue) -> Result<(), String> {
self.parent_builder
.emit_instruction(MirInstruction::Const { dst, value })
}
/// ブロック先頭に PHI 命令を挿入(不変条件: PHI は常にブロック先頭)
fn emit_phi_at_block_start(
&mut self,
block_id: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
if dbg {
eprintln!(
"[DEBUG] LoopBuilder::emit_phi_at_block_start: block={}, dst=%{}, inputs={:?}",
block_id, dst.0, inputs
);
}
// Phi nodeをブロックの先頭に挿入
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(block_id) {
if dbg {
eprintln!(
"[DEBUG] Block {} current instructions count: {}",
block_id,
block.instructions.len()
);
}
// Phi命令は必ずブロックの先頭に配置。ただし同一dstの既存PHIがある場合は差し替える。
let mut replaced = false;
let mut idx = 0;
while idx < block.instructions.len() {
match &mut block.instructions[idx] {
MirInstruction::Phi { dst: d, inputs: ins } if *d == dst => {
*ins = inputs.clone();
replaced = true;
break;
}
MirInstruction::Phi { .. } => { idx += 1; }
_ => break,
}
}
if !replaced {
let phi_inst = MirInstruction::Phi { dst, inputs: inputs.clone() };
block.instructions.insert(0, phi_inst);
}
if dbg {
eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0");
eprintln!(
"[DEBUG] Block {} after insert instructions count: {}",
block_id,
block.instructions.len()
);
}
// Verify PHI is still there
if let Some(first_inst) = block.instructions.get(0) {
match first_inst {
MirInstruction::Phi { dst: phi_dst, .. } => {
if dbg {
eprintln!(
"[DEBUG] Verified: First instruction is PHI dst=%{}",
phi_dst.0
);
}
}
other => {
if dbg {
eprintln!(
"[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}",
other
);
}
}
}
}
Ok(())
} else {
if dbg {
eprintln!("[DEBUG] ❌ Block {} not found!", block_id);
}
Err(format!("Block {} not found", block_id))
}
} else {
if dbg {
eprintln!("[DEBUG] ❌ No current function!");
}
Err("No current function".to_string())
}
}
#[allow(dead_code)]
fn add_predecessor(&mut self, block: BasicBlockId, pred: BasicBlockId) -> Result<(), String> {
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(block) {
block.add_predecessor(pred);
Ok(())
} else {
Err(format!("Block {} not found", block))
}
} else {
Err("No current function".to_string())
}
}
// =============================================================
// Variable Map Utilities — snapshots and rebinding
// =============================================================
fn get_current_variable_map(&self) -> HashMap<String, ValueId> {
self.parent_builder.variable_map.clone()
}
fn update_variable(&mut self, name: String, value: ValueId) {
if std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
eprintln!(
"[DEBUG] LoopBuilder::update_variable: name={}, value=%{}",
name, value.0
);
}
self.parent_builder.variable_map.insert(name, value);
}
fn get_variable_at_block(&self, name: &str, block_id: BasicBlockId) -> Option<ValueId> {
// まずブロックごとのスナップショットを優先
if let Some(map) = self.block_var_maps.get(&block_id) {
if let Some(v) = map.get(name) {
return Some(*v);
}
}
// フォールバック:現在の変数マップ(単純ケース用)
self.parent_builder.variable_map.get(name).copied()
}
fn build_statement(&mut self, stmt: ASTNode) -> Result<ValueId, String> {
match stmt {
// Ensure nested bare blocks inside loops are lowered with loop-aware semantics
ASTNode::Program { statements, .. } => {
let mut last = None;
for s in statements.into_iter() {
last = Some(self.build_statement(s)?);
// フェーズS修正統一終端検出ユーティリティ使用
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
Ok(last.unwrap_or_else(|| {
let void_id = self.new_value();
// Emit a void const to keep SSA consistent when block is empty
let _ = self.emit_const(void_id, ConstValue::Void);
void_id
}))
}
ASTNode::If { condition, then_body, else_body, .. } =>
self.lower_if_in_loop(*condition, then_body, else_body),
ASTNode::Break { .. } => self.do_break(),
ASTNode::Continue { .. } => self.do_continue(),
other => self.parent_builder.build_expression(other),
}
}
/// Lower an if-statement inside a loop, preserving continue/break semantics and emitting PHIs per assigned variable.
fn lower_if_in_loop(
&mut self,
condition: ASTNode,
then_body: Vec<ASTNode>,
else_body: Option<Vec<ASTNode>>,
) -> Result<ValueId, String> {
// Reserve a deterministic join id for debug region labeling (nested inside loop)
let join_id = self.parent_builder.debug_next_join_id();
// Pre-pin comparison operands to slots so repeated uses across blocks are safe
if crate::config::env::mir_pre_pin_compare_operands() {
if let ASTNode::BinaryOp { operator, left, right, .. } = &condition {
use crate::ast::BinaryOperator as BO;
match operator {
BO::Equal | BO::NotEqual | BO::Less | BO::LessEqual | BO::Greater | BO::GreaterEqual => {
if let Ok(lhs_v) = self.parent_builder.build_expression((**left).clone()) {
let _ = self.parent_builder.pin_to_slot(lhs_v, "@loop_if_lhs");
}
if let Ok(rhs_v) = self.parent_builder.build_expression((**right).clone()) {
let _ = self.parent_builder.pin_to_slot(rhs_v, "@loop_if_rhs");
}
}
_ => {}
}
}
}
// Evaluate condition and create blocks
let cond_val = self.parent_builder.build_expression(condition)?;
let then_bb = self.new_block();
let else_bb = self.new_block();
let merge_bb = self.new_block();
let pre_branch_bb = self.current_block()?;
self.emit_branch(cond_val, then_bb, else_bb)?;
// Capture pre-if variable map (used for phi normalization)
let pre_if_var_map = self.get_current_variable_map();
let trace_if = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");
// (legacy) kept for earlier merge style; now unified helpers compute deltas directly.
// then branch
self.set_current_block(then_bb)?;
// Debug region: join then-branch (inside loop)
self.parent_builder
.debug_push_region(format!("join#{}", join_id) + "/then");
// Materialize all variables at entry via single-pred Phi (correctness-first)
let names_then: Vec<String> = self
.parent_builder
.variable_map
.keys()
.filter(|n| !n.starts_with("__pin$"))
.cloned()
.collect();
for name in names_then {
if let Some(&pre_v) = pre_if_var_map.get(&name) {
let phi_val = self.new_value();
self.emit_phi_at_block_start(then_bb, phi_val, vec![(pre_branch_bb, pre_v)])?;
let name_for_log = name.clone();
self.update_variable(name, phi_val);
if trace_if {
eprintln!(
"[if-trace] then-entry phi var={} pre={:?} -> dst={:?}",
name_for_log, pre_v, phi_val
);
}
}
}
for s in then_body.iter().cloned() {
let _ = self.build_statement(s)?;
// フェーズS修正統一終端検出ユーティリティ使用
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
let then_var_map_end = self.get_current_variable_map();
// フェーズS修正最強モード指摘の「実到達predecessor捕捉」を統一
let then_pred_to_merge = capture_actual_predecessor_and_jump(
self.parent_builder,
merge_bb
)?;
// Pop then-branch debug region
self.parent_builder.debug_pop_region();
// else branch
self.set_current_block(else_bb)?;
// Debug region: join else-branch (inside loop)
self.parent_builder
.debug_push_region(format!("join#{}", join_id) + "/else");
// Materialize all variables at entry via single-pred Phi (correctness-first)
let names2: Vec<String> = self
.parent_builder
.variable_map
.keys()
.filter(|n| !n.starts_with("__pin$"))
.cloned()
.collect();
for name in names2 {
if let Some(&pre_v) = pre_if_var_map.get(&name) {
let phi_val = self.new_value();
self.emit_phi_at_block_start(else_bb, phi_val, vec![(pre_branch_bb, pre_v)])?;
let name_for_log = name.clone();
self.update_variable(name, phi_val);
if trace_if {
eprintln!(
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
name_for_log, pre_v, phi_val
);
}
}
}
let mut else_var_map_end_opt: Option<HashMap<String, ValueId>> = None;
if let Some(es) = else_body.clone() {
for s in es.into_iter() {
let _ = self.build_statement(s)?;
// フェーズS修正統一終端検出ユーティリティ使用
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
else_var_map_end_opt = Some(self.get_current_variable_map());
}
// フェーズS修正else branchでも統一実到達predecessor捕捉
let else_pred_to_merge = capture_actual_predecessor_and_jump(
self.parent_builder,
merge_bb
)?;
// Pop else-branch debug region
self.parent_builder.debug_pop_region();
// Continue at merge
self.set_current_block(merge_bb)?;
// Debug region: join merge (inside loop)
self.parent_builder
.debug_push_region(format!("join#{}", join_id) + "/join");
let mut vars: std::collections::HashSet<String> = std::collections::HashSet::new();
let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
crate::mir::phi_core::if_phi::collect_assigned_vars(&then_prog, &mut vars);
if let Some(es) = &else_body {
let else_prog = ASTNode::Program { statements: es.clone(), span: crate::ast::Span::unknown() };
crate::mir::phi_core::if_phi::collect_assigned_vars(&else_prog, &mut vars);
}
// Reset to pre-if map before rebinding to ensure a clean environment
self.parent_builder.variable_map = pre_if_var_map.clone();
// Use shared helper to merge modified variables at merge block
struct Ops<'b, 'a>(&'b mut LoopBuilder<'a>);
impl<'b, 'a> crate::mir::phi_core::if_phi::PhiMergeOps for Ops<'b, 'a> {
fn new_value(&mut self) -> ValueId { self.0.new_value() }
fn emit_phi_at_block_start(
&mut self,
block: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> { self.0.emit_phi_at_block_start(block, dst, inputs) }
fn update_var(&mut self, name: String, value: ValueId) { self.0.parent_builder.variable_map.insert(name, value); }
fn debug_verify_phi_inputs(&mut self, merge_bb: BasicBlockId, inputs: &[(BasicBlockId, ValueId)]) {
if let Some(ref func) = self.0.parent_builder.current_function {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
}
}
}
// Reset to pre-if snapshot, then delegate to shared helper
self.parent_builder.variable_map = pre_if_var_map.clone();
// Phase 25.1h: ControlForm統合版に切り替え
let if_shape = IfShape {
cond_block: pre_branch_bb,
then_block: then_bb,
else_block: Some(else_bb),
merge_block: merge_bb,
};
let form = ControlForm::from_if(if_shape.clone());
// Region/GC 観測レイヤPhase 25.1l:
// NYASH_REGION_TRACE=1 のときだけ、StageB 周辺 If 構造の
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
let mut ops = Ops(self);
crate::mir::phi_core::if_phi::merge_modified_with_control(
&mut ops,
&form,
&pre_if_var_map,
&then_var_map_end,
&else_var_map_end_opt,
None,
then_pred_to_merge,
else_pred_to_merge,
)?;
// ControlForm 観測: 環境フラグ未設定時は既定ONのとき IfShape をダンプ
if is_control_form_trace_on() {
form.debug_dump();
#[cfg(debug_assertions)]
if let Some(ref func) = self.parent_builder.current_function {
if_shape.debug_validate(func);
}
}
let void_id = self.new_value();
self.emit_const(void_id, ConstValue::Void)?;
// Pop merge debug region
self.parent_builder.debug_pop_region();
Ok(void_id)
}
}
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
fn new_value(&mut self) -> ValueId { self.new_value() }
fn emit_phi_at_block_start(
&mut self,
block: BasicBlockId,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
self.emit_phi_at_block_start(block, dst, inputs)
}
fn update_var(&mut self, name: String, value: ValueId) { self.update_variable(name, value) }
fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option<ValueId> {
// Call the inherent method (immutable borrow) to avoid recursion
LoopBuilder::get_variable_at_block(self, name, block)
}
fn debug_verify_phi_inputs(&mut self, merge_bb: BasicBlockId, inputs: &[(BasicBlockId, ValueId)]) {
if let Some(ref func) = self.parent_builder.current_function {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
}
}
fn emit_copy_at_preheader(
&mut self,
preheader_id: BasicBlockId,
dst: ValueId,
src: ValueId,
) -> Result<(), String> {
let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
if dbg {
eprintln!(
"[DEBUG] emit_copy_at_preheader: preheader={}, dst=%{}, src=%{}",
preheader_id, dst.0, src.0
);
}
if let Some(ref mut function) = self.parent_builder.current_function {
if let Some(block) = function.get_block_mut(preheader_id) {
if dbg {
eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
}
block.add_instruction(MirInstruction::Copy { dst, src });
Ok(())
} else {
if dbg {
eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id);
}
Err(format!("Preheader block {} not found", preheader_id))
}
} else {
if dbg {
eprintln!("[DEBUG] ❌ No current function!");
}
Err("No current function".to_string())
}
}
fn add_predecessor_edge(
&mut self,
block: BasicBlockId,
pred: BasicBlockId,
) -> Result<(), String> {
self.add_predecessor(block, pred)
}
}
// Implement LoopFormOps trait for LoopBuilder to support LoopFormBuilder integration
impl<'a> LoopFormOps for LoopBuilder<'a> {
fn new_value(&mut self) -> ValueId {
// CRITICAL: Must use MirFunction's next_value_id(), not MirBuilder's value_gen
// Otherwise we get SSA violations because the two counters diverge
let id = if let Some(ref mut func) = self.parent_builder.current_function {
let before = func.next_value_id;
let id = func.next_value_id();
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[LoopFormOps::new_value] fn='{}' counter: {} -> {}, allocated: {:?}",
func.signature.name, before, func.next_value_id, id);
}
id
} else {
// Fallback (should never happen in practice)
let id = self.parent_builder.value_gen.next();
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[LoopFormOps::new_value] FALLBACK value_gen, allocated: {:?}", id);
}
id
};
id
}
fn ensure_counter_after(&mut self, max_id: u32) -> Result<(), String> {
if let Some(ref mut func) = self.parent_builder.current_function {
// 📦 Hotfix 1: Consider both parameter count and existing ValueIds
let param_count = func.signature.params.len() as u32;
let min_counter = param_count.max(max_id + 1);
if func.next_value_id < min_counter {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[LoopFormOps::ensure_counter_after] fn='{}' params={}, max_id={}, adjusting counter {} -> {}",
func.signature.name, param_count, max_id, func.next_value_id, min_counter);
}
func.next_value_id = min_counter;
}
Ok(())
} else {
Err("No current function to adjust counter".to_string())
}
}
fn block_exists(&self, block: BasicBlockId) -> bool {
// 📦 Hotfix 2: Check if block exists in current function's CFG
if let Some(ref func) = self.parent_builder.current_function {
func.blocks.contains_key(&block)
} else {
false
}
}
fn get_block_predecessors(&self, block: BasicBlockId) -> std::collections::HashSet<BasicBlockId> {
// 📦 Hotfix 6: Get actual CFG predecessors for PHI validation
if let Some(ref func) = self.parent_builder.current_function {
if let Some(bb) = func.blocks.get(&block) {
bb.predecessors.clone()
} else {
std::collections::HashSet::new() // Non-existent blocks have no predecessors
}
} else {
std::collections::HashSet::new()
}
}
fn is_parameter(&self, name: &str) -> bool {
// A parameter is a true function parameter that doesn't change across iterations
// Pinned receivers (__pin$*$@*) are NOT parameters - they're carriers
// because they can be reassigned in the loop body
// Pinned variables are always carriers (loop-variant)
if name.starts_with("__pin$") {
return false;
}
// Check if it's the receiver
if name == "me" {
return true;
}
// Check if it's in the original function parameter names
// This is more reliable than checking ValueIds, which can change through copies/PHIs
let is_param = self.parent_builder.function_param_names.contains(name);
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[is_parameter] {} -> {} (param_names = {:?})",
name, is_param, self.parent_builder.function_param_names);
}
is_param
}
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
self.parent_builder.start_new_block(block)
}
fn emit_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), String> {
self.parent_builder.emit_instruction(MirInstruction::Copy { dst, src })
}
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
self.emit_jump(target)
}
fn emit_phi(
&mut self,
dst: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
self.emit_phi_at_block_start(
self.current_block()?,
dst,
inputs
)
}
fn update_phi_inputs(
&mut self,
block: BasicBlockId,
phi_id: ValueId,
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
self.parent_builder.update_phi_instruction(block, phi_id, inputs)
}
fn update_var(&mut self, name: String, value: ValueId) {
self.parent_builder.variable_map.insert(name, value);
}
fn get_variable_at_block(&self, name: &str, block: BasicBlockId) -> Option<ValueId> {
// Use the inherent method to avoid recursion
LoopBuilder::get_variable_at_block(self, name, block)
}
}