use super::{ConstValue, LoopBuilder, ValueId}; use crate::ast::ASTNode; use crate::mir::control_form::{is_control_form_trace_on, ControlForm, LoopShape}; use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps}; use crate::mir::utils::is_current_block_terminated; use crate::mir::{BasicBlockId, MirInstruction}; use std::collections::{BTreeMap, BTreeSet}; impl<'a> LoopBuilder<'a> { /// SSA形式でループを構築 (LoopForm v2 only) pub fn build_loop( &mut self, condition: ASTNode, body: Vec, ) -> Result { // 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, ) -> 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() { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform] before_loop_id={:?}, variable_map size={}", before_loop_id, current_vars.len() )); for (name, value) in ¤t_vars { crate::runtime::get_global_ring0().log.debug(&format!(" {} -> {:?}", name, value)); } } // Phase 25.3: GUARD check removed - ValueId(0) is valid for first parameters // Previous code incorrectly assumed ValueId(0) always meant uninitialized variables, // but it's actually the correct ID for the first parameter in functions like: // skip_whitespace(s, idx) -> s=ValueId(0), idx=ValueId(1) // This caused loops in such functions to be entirely skipped. 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(); // Phase 25.1q: canonical continue merge block // All continue 文は一度このブロックに集約してから header へ戻る。 let continue_merge_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() { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}", preheader_id, header_id, body_id, latch_id, exit_id )); crate::runtime::get_global_ring0().log.debug(&format!( "[loopform] variable_map at loop entry (size={}):", current_vars.len() )); let mut loop_count = 0; for (name, value) in ¤t_vars { loop_count += 1; crate::runtime::get_global_ring0().log.debug(&format!(" [{}] {} -> {:?}", loop_count, name, value)); // Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全) let is_param = self.is_parameter(*value); crate::runtime::get_global_ring0().log.debug(&format!(" param={}", is_param)); } crate::runtime::get_global_ring0().log.debug(&format!("[loopform] iterated {} times", loop_count)); if let Some(ref func) = self.parent_builder.current_function { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}", func.signature.name, func.next_value_id, func as *const _ )); } else { crate::runtime::get_global_ring0().log.debug("[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 { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}", func.signature.name, func.next_value_id, func as *const _ )); } else { crate::runtime::get_global_ring0().log.debug("[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)?; // Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグ統一取得 let fn_name = self .parent_builder .current_function .as_ref() .map(|f| f.signature.name.clone()) .unwrap_or_default(); let bypass_flags = crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name); if bypass_flags.header { // Phase 27.4-C: JoinIR 実験経路では Header φ を生成しない。 // Pinned/Carrier の値は preheader の copy をそのまま使う。 // // ⚠️ 重要: このモードでは MIR は不完全(φ 抜け)であり、VM で実行できない。 // JoinIR runner 専用モードであることに注意。 if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() { crate::runtime::get_global_ring0().log.debug(&format!("[loopform/27.4-C] Header φ bypass active for: {}", fn_name)); crate::runtime::get_global_ring0().log.debug("[loopform/27.4-C] Skipping emit_header_phis() - using preheader values directly"); } } else { // 従来どおり HeaderPhiBuilder を使って φ を準備 loopform.emit_header_phis(self)?; } if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() { crate::runtime::get_global_ring0().log.debug("[loopform] variable_map after emit_header_phis:"); for (name, value) in self.get_current_variable_map().iter() { crate::runtime::get_global_ring0().log.debug(&format!(" {} -> {:?}", 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); // 既定の continue 先を canonical continue_merge ブロックにする。 // ここを差し替えることで do_loop_exit(Continue) のターゲットを一元化する。 self.continue_target = Some(continue_merge_id); self.continue_snapshots.clear(); self.exit_snapshots.clear(); // [LoopForm] header-cond: cond true → body, false → exit (Case A/B) // - Case A: loop(i < n) → header can branch to exit directly // - Case B: loop(1 == 1) → header always enters body, exit only via break if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform/condition] BEFORE build_expression: current_block={:?}", self.current_block()? )); if let Some(ref func) = self.parent_builder.current_function { crate::runtime::get_global_ring0().log.debug(&format!( "[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() { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform/condition] AFTER build_expression: branch_source_block={:?}", branch_source_block )); if let Some(ref func) = self.parent_builder.current_function { crate::runtime::get_global_ring0().log.debug(&format!( "[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() { crate::runtime::get_global_ring0().log.debug(&format!( "[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() { crate::runtime::get_global_ring0().log.debug(&format!( "[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()? )); crate::runtime::get_global_ring0().log.debug(&format!( "[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) { crate::runtime::get_global_ring0().log.debug(&format!( "[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(); // Step 5-1: Writes集合収集(選択肢2+3統合: Snapshot比較で再代入検出) // current_vars (preheader) と body_end_vars を比較し、ValueId が変わった変数を特定 use std::collections::HashSet; let mut writes = HashSet::new(); for (name, &body_value) in &body_end_vars { // Skip __pin$ temporary variables - they are always BodyLocalInternal // (Task先生の発見: これらをcarrier扱いすると未定義ValueIdエラーの原因になる) if name.starts_with("__pin$") && name.contains("$@") { continue; } if let Some(&base_value) = current_vars.get(name) { if body_value != base_value { writes.insert(name.clone()); } } // else: body で新規定義された変数(body-local)、header PHI 不要 } // DEBUG: Log writes collection if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { crate::runtime::get_global_ring0().log.debug("[loopform/writes] === WRITES COLLECTION (Step 5-1) ==="); crate::runtime::get_global_ring0().log.debug(&format!( "[loopform/writes] {} variables modified in loop body", writes.len() )); let mut sorted_writes: Vec<_> = writes.iter().collect(); sorted_writes.sort(); for name in &sorted_writes { crate::runtime::get_global_ring0().log.debug(&format!("[loopform/writes] WRITE: {}", name)); } } // 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.clone(), *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)?; // Phase 25.1c/k: body-local 変数の PHI 生成 // BreakFinderBox / FuncScannerBox 等で、loop body 内で新規宣言された local 変数が // loop header に戻った時に undefined になる問題を修正 // // Step 5-5-B: EXPERIMENTAL - Body-local Header PHI generation DISABLED // Reason: Option C design states body-local variables should NOT have header PHIs // - BodyLocalExit: needs EXIT PHI only, NOT header PHI // - BodyLocalInternal: needs NO PHI at all // // TODO Step 5-3: Integrate Option C classification (LoopScopeShape) here // // TEMPORARY DISABLE to test hypothesis that header PHIs are the root cause let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1"); // DISABLED: Body-local header PHI generation // This code was causing undefined value errors because it created header PHIs // for variables that should only have exit PHIs (or no PHIs at all) if false { // Disabled for Step 5-5-B experiment // [Original code removed - see git history if needed] } // Pass 4: Generate continue_merge PHIs first, then seal header PHIs // Phase 25.1c/k: canonical continue_merge ブロックで PHI を生成してから seal_phis を呼ぶ let raw_continue_snaps = self.continue_snapshots.clone(); // Step 1: continue_merge ブロックで PHI 生成(merged_snapshot を作る) // Phase 25.2: LoopSnapshotMergeBox を使って整理 self.set_current_block(continue_merge_id)?; let merged_snapshot: BTreeMap = if !raw_continue_snaps.is_empty() { if trace_loop_phi { eprintln!( "[loop-phi/continue-merge] Generating PHI nodes for {} continue paths", raw_continue_snaps.len() ); } // すべての continue snapshot に現れる変数を収集 let mut all_vars: BTreeMap> = BTreeMap::new(); for (continue_bb, snapshot) in &raw_continue_snaps { for (var_name, &value) in snapshot { all_vars .entry(var_name.clone()) .or_default() .push((*continue_bb, value)); } } // 各変数について PHI ノードを生成 // ======================================== // Phase 59b: PhiInputCollector インライン化 // ======================================== let mut merged = BTreeMap::new(); for (var_name, inputs) in all_vars { // Step 1: sanitize (BTreeMap で重複削除&ソート) let mut sanitized: BTreeMap = BTreeMap::new(); for (bb, val) in &inputs { sanitized.insert(*bb, *val); } let final_inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect(); // Step 2: optimize_same_value let same_value = if final_inputs.is_empty() { None } else if final_inputs.len() == 1 { Some(final_inputs[0].1) } else { let first_val = final_inputs[0].1; if final_inputs.iter().all(|(_, val)| *val == first_val) { Some(first_val) } else { None } }; // Step 3: PHI 生成 or 同一値を使用 let result_value = if let Some(same_val) = same_value { // 全て同じ値 or 単一入力 → PHI 不要 same_val } else { // 異なる値を持つ場合は PHI ノードを生成 let phi_id = self.new_value(); if let Some(ref mut func) = self.parent_builder.current_function { if let Some(merge_block) = func.blocks.get_mut(&continue_merge_id) { merge_block.add_instruction(MirInstruction::Phi { dst: phi_id, inputs: final_inputs, type_hint: None, // Phase 63-6 }); } } if trace_loop_phi { eprintln!( "[loop-phi/continue-merge] Generated PHI for '{}': {:?}", var_name, phi_id ); } phi_id }; merged.insert(var_name, result_value); } // Note: 変数マップへの反映は seal_phis に委譲(干渉を避ける) if trace_loop_phi { eprintln!( "[loop-phi/continue-merge] Merged {} variables from {} paths", merged.len(), raw_continue_snaps.len() ); } merged } else { BTreeMap::new() }; self.emit_jump(header_id)?; crate::mir::builder::loops::add_predecessor( self.parent_builder, header_id, continue_merge_id, )?; // Step 2: merged_snapshot を使って seal_phis を呼ぶ // Phase 25.3: Continue merge PHI実装(Task先生の発見!) // - continueが無いループでも、Latchブロックの値をHeader PHIに伝播する必要がある // - これにより、Exit PHIがHeader PHI経由で正しい値を受け取れる let continue_snaps: Vec<(BasicBlockId, BTreeMap)> = { // まず、merged_snapshot(continue merge PHI結果)を追加 let mut snaps = if !merged_snapshot.is_empty() { vec![(continue_merge_id, merged_snapshot.clone())] } else { vec![] }; // continueが無い場合でも、Latchブロックのスナップショットを追加 // これにより、seal_phis()がLatchからの値をHeader PHIに正しく接続できる if raw_continue_snaps.is_empty() { // continue文が無い場合、Latchブロックの現在の変数マップをキャプチャ // Note: このタイミングでは current_block == exit_id だが、 // variable_map はLatch実行後の状態を保持している let latch_snapshot = self.get_current_variable_map(); snaps.push((actual_latch_id, latch_snapshot)); } snaps }; // Phase 27.4C Refactor: Header φ バイパスフラグを統一取得(seal_phis に渡す) // Note: fn_name は既に line 299-304 で取得済み、String として保持されている let bypass_flags_for_seal = crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name); // Step 5-1/5-2: Pass writes 集合 for PHI縮約 // Phase 27.4C: header_bypass フラグも渡す loopform.seal_phis( self, actual_latch_id, &continue_snaps, &writes, bypass_flags_for_seal.header, )?; // Step 3: seal body-local PHIs (complete the inputs) // Step 5-5-A: REMOVED - PHIs now created complete with both inputs upfront // Old sealing code was overwriting our preheader+latch inputs with latch-only, // causing "phi pred mismatch" errors. // // Body-local PHIs are now created at line 408-456 with BOTH inputs: // - preheader: poison value (variable doesn't exist yet) // - latch: actual value from loop body // // No further sealing is needed! // Exit block self.set_current_block(exit_id)?; // Phase 25.1h: ControlForm統合版に切り替え // continue / break のターゲットブロックをユニーク化して収集 // Phase 25.1: HashSet → BTreeSet(決定性確保) let mut break_set: BTreeSet = BTreeSet::new(); for (bb, _) in &self.exit_snapshots { break_set.insert(*bb); } // LoopShape の continue_targets は「header への canonical backedge」を表す。 // continue が一つ以上存在する場合は continue_merge_id を 1 つだけ登録する。 let continue_targets: Vec = if self.continue_snapshots.is_empty() { Vec::new() } else { vec![continue_merge_id] }; let break_targets: Vec = 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 のときだけ、Stage‑B 周辺ループの // Region 情報(entry/exit/slots)をログに出すよ。 crate::mir::region::observer::observe_control_form(self.parent_builder, &form); // Phase 27.6-2: JoinIR Exit φ バイパスチェック let fn_name = self .parent_builder .current_function .as_ref() .map(|f| f.signature.name.as_str()) .unwrap_or(""); let exit_bypass = crate::mir::phi_core::loopform_builder::joinir_exit_bypass_enabled() && crate::mir::phi_core::loopform_builder::is_joinir_exit_bypass_target(fn_name); if exit_bypass { // Phase 27.6-2: JoinIR 実験経路では Exit φ を生成しない。 // ループ内で定義された値だけで exit 後を構成する(JoinIR の k_exit 引数として表現)。 // // ⚠️ 重要: このモードでは MIR は不完全(Exit φ 抜け)であり、VM で実行できない。 // JoinIR runner 専用モードであることに注意。 if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { eprintln!( "[loopform/exit-bypass] func={} exit={:?} header={:?} (JoinIR experiment only)", fn_name, exit_id, header_id ); } } else { // [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds) // - Case A: header+break → exit PHI includes both paths // - Case B: break-only → exit PHI excludes header (not a predecessor) 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) } }