/*! * 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::phi_core::loop_phi::IncompletePhi; 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, }; // IncompletePhi has moved to phi_core::loop_phi /// ループビルダー - SSA形式でのループ構築を管理 pub struct LoopBuilder<'a> { /// 親のMIRビルダーへの参照 parent_builder: &'a mut super::builder::MirBuilder, /// ループ内で追跡する変数の不完全Phi node incomplete_phis: HashMap>, /// ブロックごとの変数マップ(スコープ管理) #[allow(dead_code)] block_var_maps: HashMap>, /// ループヘッダーID(continueで使用) loop_header: Option, /// continue文からの変数スナップショット continue_snapshots: Vec<(BasicBlockId, HashMap)>, /// break文からの変数スナップショット(exit PHI生成用) exit_snapshots: Vec<(BasicBlockId, HashMap)>, // フェーズ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 fn find_copy_source(&self, block_id: BasicBlockId, dst: ValueId) -> Option { let func = self.parent_builder.current_function.as_ref()?; let block = func.blocks.get(&block_id)?; let trace = std::env::var("NYASH_LOOP_TRACE").ok().as_deref() == Some("1"); if trace { eprintln!("[loop/copy-trace] searching for dst={:?} in block={:?}, {} instructions", dst, block_id, block.instructions.len()); } for (idx, inst) in block.instructions.iter().enumerate() { if let MirInstruction::Copy { dst: inst_dst, src } = inst { if trace { eprintln!("[loop/copy-trace] inst#{}: %{} = copy %{}", idx, inst_dst.0, src.0); } if *inst_dst == dst { if trace { eprintln!("[loop/copy-trace] FOUND! dst={:?} src={:?}", dst, src); } return Some(*src); } } } if trace { eprintln!("[loop/copy-trace] NOT FOUND for dst={:?}", dst); } 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 { 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) } /// Handle a `break` statement: jump to loop exit and continue in a fresh unreachable block. fn do_break(&mut self) -> Result { // Snapshot variables at break point for exit PHI generation let snapshot = self.get_current_variable_map(); let cur_block = self.current_block()?; eprintln!("[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}", cur_block, snapshot.keys().collect::>()); self.exit_snapshots.push((cur_block, snapshot)); if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) { self.jump_with_pred(exit_bb)?; } self.switch_to_unreachable_block_with_void() } /// Handle a `continue` statement: snapshot vars, jump to loop header, then continue in a fresh unreachable block. fn do_continue(&mut self) -> Result { // Snapshot variables at current block to be considered as a predecessor input let snapshot = self.get_current_variable_map(); let cur_block = self.current_block()?; self.block_var_maps.insert(cur_block, snapshot.clone()); self.continue_snapshots.push((cur_block, snapshot)); if let Some(header) = self.loop_header { self.jump_with_pred(header)?; } self.switch_to_unreachable_block_with_void() } // ============================================================= // Lifecycle — create builder, main loop construction // ============================================================= /// 新しいループビルダーを作成 pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self { // フェーズM: no_phi_mode初期化削除 Self { parent_builder: parent, incomplete_phis: HashMap::new(), block_var_maps: HashMap::new(), loop_header: None, continue_snapshots: Vec::new(), exit_snapshots: Vec::new(), // exit PHI用のスナップショット // フェーズM: no_phi_modeフィールド削除 } } /// SSA形式でループを構築 (Feature flag dispatch) pub fn build_loop( &mut self, condition: ASTNode, body: Vec, ) -> Result { // Check feature flag for LoopForm PHI v2 let use_loopform_v2 = std::env::var("NYASH_LOOPFORM_PHI_V2") .map(|v| v == "1" || v.to_lowercase() == "true") .unwrap_or(false); if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { eprintln!("[build_loop] use_loopform_v2={}", use_loopform_v2); } if use_loopform_v2 { self.build_loop_with_loopform(condition, body) } else { eprintln!("⚠️ WARNING: Using legacy loop builder! Set NYASH_LOOPFORM_PHI_V2=1"); self.build_loop_legacy(condition, body) } } /// SSA形式でループを構築 (LoopFormBuilder implementation) fn build_loop_with_loopform( &mut self, condition: ASTNode, body: Vec, ) -> Result { 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 ¤t_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 ¤t_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 self.emit_jump(preheader_id)?; // 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 ¤t_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, ¤t_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)?; // 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)?; if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?); } // 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)? { let cur_body_end = self.current_block()?; 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)?; // Pass 4: Seal PHIs with latch values loopform.seal_phis(self, actual_latch_id)?; // Exit block self.set_current_block(exit_id)?; // Build exit PHIs for break statements let exit_snaps = self.exit_snapshots.clone(); loopform.build_exit_phis(self, exit_id, branch_source_block, &exit_snaps)?; // Pop loop context crate::mir::builder::loops::pop_loop_context(self.parent_builder); // Return void value let void_dst = self.new_value(); self.emit_const(void_dst, ConstValue::Void)?; Ok(void_dst) } /// SSA形式でループを構築 (Legacy implementation) fn build_loop_legacy( &mut self, condition: ASTNode, body: Vec, ) -> Result { // Reserve a deterministic loop id for debug region labeling let loop_id = self.parent_builder.debug_next_loop_id(); // Pre-scan body for simple carrier pattern (up to 2 assigned variables, no break/continue) let mut assigned_vars: Vec = Vec::new(); let mut has_ctrl = false; for st in &body { crate::mir::phi_core::loop_phi::collect_carrier_assigns(st, &mut assigned_vars, &mut has_ctrl); } if !has_ctrl && !assigned_vars.is_empty() && assigned_vars.len() <= 2 { // Emit a carrier hint (no-op sink by default; visible with NYASH_MIR_TRACE_HINTS=1) self.parent_builder.hint_loop_carrier(assigned_vars.clone()); } // 1. ブロックの準備 let preheader_id = self.current_block()?; // Snapshot variable map at preheader before switching to header to avoid // capturing block-local SSA placeholders created on block switch. let pre_vars_snapshot = self.get_current_variable_map(); let trace = std::env::var("NYASH_LOOP_TRACE").ok().as_deref() == Some("1"); let (header_id, body_id, after_loop_id) = crate::mir::builder::loops::create_loop_blocks(self.parent_builder); if trace { eprintln!( "[loop] blocks preheader={:?} header={:?} body={:?} exit={:?}", preheader_id, header_id, body_id, after_loop_id ); } self.loop_header = Some(header_id); self.continue_snapshots.clear(); // 2. Preheader -> Header へのジャンプ self.emit_jump(header_id)?; let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id); // 3. Headerブロックの準備(unsealed状態) self.set_current_block(header_id)?; // Debug region: loop header self.parent_builder .debug_push_region(format!("loop#{}", loop_id) + "/header"); // Hint: loop header (no-op sink) self.parent_builder.hint_loop_header(); let _ = self.mark_block_unsealed(header_id); // 4. ループ変数のPhi nodeを準備 // ここでは、ループ内で変更される可能性のある変数を事前に検出するか、 // または変数アクセス時に遅延生成する(再束縛は条件式構築後に行う) let incs = self.prepare_loop_variables(header_id, preheader_id, &pre_vars_snapshot, &assigned_vars)?; // 5. 条件評価(Phi nodeの結果を使用) // Heuristic pre-pin: if condition is a comparison, evaluate its operands and pin them // so that the loop body/next iterations can safely reuse these values across blocks. 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"); } } _ => {} } } } let condition_value = self.build_expression_with_phis(condition)?; // Fix for ValueId(17) bug: Add PHI nodes for any pinned variables created during condition evaluation // When method calls occur in loop conditions (e.g., i < args.length()), pin_to_slot creates // pinned receiver variables like __pin$*@recv. These must have PHI nodes in the loop header. let post_cond_vars = self.get_current_variable_map(); let mut new_pinned_vars: Vec<(String, ValueId, ValueId)> = Vec::new(); if trace { eprintln!("[loop] post_cond_vars has {} entries", post_cond_vars.len()); } for (name, &value) in post_cond_vars.iter() { if !name.starts_with("__pin$") { continue; } // Check if this pinned variable existed before condition compilation let was_in_incs = incs.iter().any(|inc| inc.var_name == *name); let was_in_preheader = pre_vars_snapshot.contains_key(name); if !was_in_incs && !was_in_preheader { // This is a NEW pinned variable created during condition evaluation (not inherited from preheader) // We need to find the source of the copy instruction that created this value let preheader_value = self.find_copy_source(header_id, value).unwrap_or(value); if trace { eprintln!("[loop] NEW pinned var: {} value={:?} preheader={:?}", name, value, preheader_value); } new_pinned_vars.push((name.clone(), value, preheader_value)); } else if !was_in_incs && was_in_preheader { // This pinned variable existed in preheader, so it needs a PHI but we should use the preheader value let preheader_value = pre_vars_snapshot.get(name).copied().unwrap_or(value); if trace { eprintln!("[loop] INHERITED pinned var: {} value={:?} preheader={:?}", name, value, preheader_value); } new_pinned_vars.push((name.clone(), value, preheader_value)); } } // Add PHI nodes for new pinned variables in header block for (name, value, preheader_value) in new_pinned_vars { let phi_id = self.new_value(); self.emit_phi_at_block_start(header_id, phi_id, vec![(preheader_id, preheader_value)])?; // Update variable map to use PHI value self.update_variable(name.clone(), phi_id); // Add to incomplete PHIs for later sealing with latch edges if let Some(ref mut header_incs) = self.incomplete_phis.get_mut(&header_id) { header_incs.push(crate::mir::phi_core::loop_phi::IncompletePhi { phi_id, var_name: name, known_inputs: vec![(preheader_id, preheader_value)], }); } } // 6. 条件分岐 let pre_branch_bb = self.current_block()?; // Fix for ValueId UseBeforeDef bug: Build header snapshot from PHI values // The exit PHI must reference values that are defined AT THE HEADER BLOCK'S EXIT. // We can't use the current variable_map directly because it might contain values // that are only partially defined. Instead, use the PHI values from incomplete_phis // and any new pinned variables created during condition evaluation. let mut header_exit_snapshot = std::collections::HashMap::new(); // First, collect all PHI values (these are defined at the header entry) for inc in &incs { header_exit_snapshot.insert(inc.var_name.clone(), inc.phi_id); } // Then, add any new pinned variables created during condition evaluation // (these were added as PHIs in lines 204-219) let post_cond_vars = self.get_current_variable_map(); for (name, &value) in post_cond_vars.iter() { if !header_exit_snapshot.contains_key(name) { header_exit_snapshot.insert(name.clone(), value); } } self.emit_branch(condition_value, body_id, after_loop_id)?; let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, header_id); let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, after_loop_id, header_id); if trace { eprintln!( "[loop] header branched to body={:?} and exit={:?}", body_id, after_loop_id ); } // Save the header snapshot for exit PHI generation crate::mir::phi_core::loop_phi::save_block_snapshot( &mut self.block_var_maps, header_id, &header_exit_snapshot, ); // Rebind loop-carried variables to their PHI IDs now that condition is emitted for inc in &incs { self.update_variable(inc.var_name.clone(), inc.phi_id); } // 7. ループボディの構築 self.set_current_block(body_id)?; // Debug region: loop body self.parent_builder .debug_replace_region(format!("loop#{}", loop_id) + "/body"); // Materialize pinned slots at entry via single-pred Phi // IMPORTANT: Use header_exit_snapshot, not current variable_map, to avoid // referencing values that are defined after the header's branch instruction. let names: Vec = header_exit_snapshot.keys().cloned().collect(); for name in names { if !name.starts_with("__pin$") { continue; } if let Some(&pre_v) = header_exit_snapshot.get(&name) { let phi_val = self.new_value(); self.emit_phi_at_block_start(body_id, phi_val, vec![(pre_branch_bb, pre_v)])?; self.update_variable(name, phi_val); } } // Scope enter for loop body self.parent_builder.hint_scope_enter(0); // Optional safepoint per loop-iteration if std::env::var("NYASH_BUILDER_SAFEPOINT_LOOP") .ok() .as_deref() == Some("1") { self.emit_safepoint()?; } // ボディをビルド for stmt in body { self.build_statement(stmt)?; } // 8. Latchブロック(ボディの最後)からHeaderへ戻る // 現在の挿入先が latch(最後のブロック)なので、そのブロックIDでスナップショットを保存する let latch_id = self.current_block()?; // Hint: loop latch (no-op sink) self.parent_builder.hint_loop_latch(); // Debug region: loop latch (end of body) self.parent_builder .debug_replace_region(format!("loop#{}", loop_id) + "/latch"); // Scope leave for loop body self.parent_builder.hint_scope_leave(0); let latch_snapshot = self.get_current_variable_map(); // 以前は body_id に保存していたが、複数ブロックのボディや continue 混在時に不正確になるため // 実際の latch_id に対してスナップショットを紐づける crate::mir::phi_core::loop_phi::save_block_snapshot( &mut self.block_var_maps, latch_id, &latch_snapshot, ); // Only jump back to header if the latch block is not already terminated { let need_jump = { if let Some(ref fun_ro) = self.parent_builder.current_function { if let Some(bb) = fun_ro.get_block(latch_id) { !bb.is_terminated() } else { true } } else { true } }; if need_jump { self.emit_jump(header_id)?; let _ = crate::mir::builder::loops::add_predecessor( self.parent_builder, header_id, latch_id, ); } } // 9. Headerブロックをシール(全predecessors確定) self.seal_block(header_id, latch_id)?; if trace { eprintln!( "[loop] sealed header={:?} with latch={:?}", header_id, latch_id ); } // 10. ループ後の処理 - Exit PHI生成 self.set_current_block(after_loop_id)?; // Debug region: loop exit self.parent_builder .debug_replace_region(format!("loop#{}", loop_id) + "/exit"); // Exit PHIの生成 - break時点での変数値を統一 self.create_exit_phis(header_id, after_loop_id)?; // Pop loop context crate::mir::builder::loops::pop_loop_context(self.parent_builder); // Pop debug region scope self.parent_builder.debug_pop_region(); // void値を返す let void_dst = self.new_value(); self.emit_const(void_dst, ConstValue::Void)?; if trace { eprintln!("[loop] exit={:?} return void=%{:?}", after_loop_id, void_dst); } Ok(void_dst) } // ============================================================= // PHI Helpers — prepare/finalize PHIs and block sealing // ============================================================= /// ループ変数の準備(事前検出または遅延生成) /// /// ポリシー: /// - ループキャリア(ループ本体で再代入される変数)と pinned 変数のみを PHI 対象とする。 /// - ループ不変のローカル(text_len / pattern_len など)は preheader 値をそのまま使い、 /// 不要な PHI を張らないことで SSA 破綻(同一 ValueId の二重定義)を防ぐ。 fn prepare_loop_variables( &mut self, header_id: BasicBlockId, preheader_id: BasicBlockId, pre_vars_snapshot: &std::collections::HashMap, assigned_vars: &[String], ) -> Result, String> { use std::sync::atomic::{AtomicUsize, Ordering}; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst); // Use the variable map captured at preheader (before switching to header), // but filter to: // - ループキャリア(assigned_vars に含まれる変数) // - pinned 変数(__pin$*): 受信箱など、ループをまたいで値を運ぶ必要があるもの let mut current_vars = std::collections::HashMap::new(); for (name, &val) in pre_vars_snapshot.iter() { if name.starts_with("__pin$") { current_vars.insert(name.clone(), val); continue; } if assigned_vars.iter().any(|v| v == name) { current_vars.insert(name.clone(), val); } } // Debug: print current_vars before prepare (guarded by env) let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1"); if dbg { eprintln!("[DEBUG] prepare_loop_variables call #{}", count); eprintln!("[DEBUG] current_vars = {:?}", current_vars); eprintln!( "[DEBUG] preheader_id = {:?}, header_id = {:?}", preheader_id, header_id ); } crate::mir::phi_core::loop_phi::save_block_snapshot( &mut self.block_var_maps, preheader_id, ¤t_vars, ); let incs = crate::mir::phi_core::loop_phi::prepare_loop_variables_with( self, header_id, preheader_id, ¤t_vars, )?; // Defer variable rebinding to PHI IDs until after the loop condition is emitted. // Store incomplete PHIs for later sealing and for rebinding after branch emission. self.incomplete_phis.insert(header_id, incs.clone()); Ok(incs) } /// ブロックをシールし、不完全なPhi nodeを完成させる fn seal_block(&mut self, block_id: BasicBlockId, latch_id: BasicBlockId) -> Result<(), String> { if let Some(incomplete_phis) = self.incomplete_phis.remove(&block_id) { let cont_snaps = self.continue_snapshots.clone(); crate::mir::phi_core::loop_phi::seal_incomplete_phis_with( self, block_id, latch_id, incomplete_phis, &cont_snaps, )?; } self.mark_block_sealed(block_id)?; Ok(()) } /// Exitブロックで変数のPHIを生成(breakポイントでの値を統一) fn create_exit_phis(&mut self, header_id: BasicBlockId, exit_id: BasicBlockId) -> Result<(), String> { // Use the saved header block snapshot instead of current variable map // The current block at this point is the exit block, not the header, // so we must retrieve the header's snapshot from block_var_maps. let header_vars = self.block_var_maps .get(&header_id) .cloned() .unwrap_or_else(|| self.get_current_variable_map()); let exit_snaps = self.exit_snapshots.clone(); crate::mir::phi_core::loop_phi::build_exit_phis_with( self, header_id, exit_id, &header_vars, &exit_snaps, ) } // --- ヘルパーメソッド(親ビルダーへの委譲) --- fn current_block(&self) -> Result { 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_safepoint(&mut self) -> Result<(), String> { self.parent_builder .emit_instruction(MirInstruction::Safepoint) } 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()) } } fn mark_block_unsealed(&mut self, _block_id: BasicBlockId) -> Result<(), String> { // ブロックはデフォルトでunsealedなので、特に何もしない // (既にBasicBlock::newでsealed: falseに初期化されている) Ok(()) } fn mark_block_sealed(&mut self, block_id: BasicBlockId) -> Result<(), String> { if let Some(ref mut function) = self.parent_builder.current_function { if let Some(block) = function.get_block_mut(block_id) { block.seal(); Ok(()) } else { Err(format!("Block {} not found", block_id)) } } else { Err("No current function".to_string()) } } // ============================================================= // Variable Map Utilities — snapshots and rebinding // ============================================================= fn get_current_variable_map(&self) -> HashMap { 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 { // まずブロックごとのスナップショットを優先 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_expression_with_phis(&mut self, expr: ASTNode) -> Result { // Phi nodeの結果を考慮しながら式を構築 self.parent_builder.build_expression(expr) } fn build_statement(&mut self, stmt: ASTNode) -> Result { 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, else_body: Option>, ) -> Result { // 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 = 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 = 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> = 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 = 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(); let mut ops = Ops(self); crate::mir::phi_core::if_phi::merge_modified_at_merge_with( &mut ops, merge_bb, then_bb, else_bb, then_pred_to_merge, else_pred_to_merge, &pre_if_var_map, &then_var_map_end, &else_var_map_end_opt, None, )?; 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 { // 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 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 { // Use the inherent method to avoid recursion LoopBuilder::get_variable_at_block(self, name, block) } }