diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 81026d14..51f48883 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -335,6 +335,30 @@ impl<'a> LoopBuilder<'a> { // 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 { + 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() { + eprintln!("[loopform/writes] === WRITES COLLECTION (Step 5-1) ==="); + eprintln!("[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 { + eprintln!("[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)?; @@ -487,7 +511,8 @@ impl<'a> LoopBuilder<'a> { } else { vec![(continue_merge_id, merged_snapshot.clone())] }; - loopform.seal_phis(self, actual_latch_id, &continue_snaps)?; + // Step 5-1/5-2: Pass writes 集合 for PHI縮約 + loopform.seal_phis(self, actual_latch_id, &continue_snaps, &writes)?; // Step 3: seal body-local PHIs (complete the inputs) if !body_local_vars.is_empty() { @@ -547,7 +572,6 @@ impl<'a> LoopBuilder<'a> { // Phase 25.1h: ControlForm統合版に切り替え // continue / break のターゲットブロックをユニーク化して収集 - use std::collections::HashSet; let mut break_set: HashSet = HashSet::new(); for (bb, _) in &self.exit_snapshots { break_set.insert(*bb); diff --git a/src/mir/phi_core/loopform_builder.rs b/src/mir/phi_core/loopform_builder.rs index d4ba6c17..293ecedf 100644 --- a/src/mir/phi_core/loopform_builder.rs +++ b/src/mir/phi_core/loopform_builder.rs @@ -84,6 +84,9 @@ pub struct LoopFormBuilder { pub pinned: Vec, pub preheader_id: BasicBlockId, pub header_id: BasicBlockId, + /// Step 5-2: Preheader snapshot for ValueId comparison (選択肢3) + /// Used in seal_phis() to detect which variables are truly modified vs. invariant + pub preheader_vars: HashMap, } impl LoopFormBuilder { @@ -94,6 +97,7 @@ impl LoopFormBuilder { pinned: Vec::new(), preheader_id, header_id, + preheader_vars: HashMap::new(), // Will be set in prepare_structure() } } @@ -107,8 +111,19 @@ impl LoopFormBuilder { ops: &mut O, current_vars: &HashMap, ) -> Result<(), String> { - if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { + let debug_enabled = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok(); + + // Step 5-2: Save preheader snapshot for ValueId comparison in seal_phis() + self.preheader_vars = current_vars.clone(); + + if debug_enabled { eprintln!("[loopform/prepare] === START prepare_structure() === {} variables", current_vars.len()); + eprintln!("[loopform/prepare] Full variable list:"); + let mut sorted_vars: Vec<_> = current_vars.iter().collect(); + sorted_vars.sort_by_key(|(name, _)| name.as_str()); + for (name, value) in &sorted_vars { + eprintln!("[loopform/prepare] - {} = {:?}", name, value); + } } // GUARD: Detect invalid ValueId in variable map @@ -116,7 +131,7 @@ impl LoopFormBuilder { // Skip this loop construction attempt if detected (likely a premature build) for (name, &value) in current_vars.iter() { if value == ValueId::INVALID { - if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { + if debug_enabled { eprintln!("[loopform/prepare] ⚠️ GUARD: Skipping loop preparation due to invalid ValueId for variable '{}'", name); eprintln!("[loopform/prepare] This indicates the loop is being built prematurely before variables are defined"); eprintln!("[loopform/prepare] Returning Ok(()) to allow retry with properly initialized variables"); @@ -130,17 +145,21 @@ impl LoopFormBuilder { // Without this, new_value() can return ValueIds that are already in use let max_existing_id = current_vars.values().map(|v| v.0).max().unwrap_or(0); - if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { + if debug_enabled { eprintln!("[loopform/prepare] Calling ensure_counter_after(max_existing_id={})", max_existing_id); - eprintln!("[loopform/prepare] current_vars: {:?}", current_vars); } ops.ensure_counter_after(max_existing_id)?; + // Count variables by classification for summary + let mut param_count = 0; + let mut carrier_count = 0; + // Separate variables into carriers and pinned based on parameter status for (name, &value) in current_vars.iter() { // Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全) if ops.is_parameter(value) { + param_count += 1; // Pinned variable (parameter, not modified in loop) let pinned = PinnedVariable { name: name.clone(), @@ -148,12 +167,13 @@ impl LoopFormBuilder { preheader_copy: ops.new_value(), // Allocate NOW header_phi: ops.new_value(), // Allocate NOW }; - if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { - eprintln!("[loopform/prepare] pinned: {} -> init={:?}, copy={:?}, phi={:?}", + if debug_enabled { + eprintln!("[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}", name, value, pinned.preheader_copy, pinned.header_phi); } self.pinned.push(pinned); } else { + carrier_count += 1; // Carrier variable (local, modified in loop) let carrier = CarrierVariable { name: name.clone(), @@ -162,14 +182,21 @@ impl LoopFormBuilder { header_phi: ops.new_value(), // Allocate NOW latch_value: ValueId(0), // Will be set during seal (placeholder) }; - if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { - eprintln!("[loopform/prepare] carrier: {} -> init={:?}, copy={:?}, phi={:?}", + if debug_enabled { + eprintln!("[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}", name, value, carrier.preheader_copy, carrier.header_phi); } self.carriers.push(carrier); } } + if debug_enabled { + eprintln!("[loopform/prepare] === SUMMARY ==="); + eprintln!("[loopform/prepare] Total vars: {}", current_vars.len()); + eprintln!("[loopform/prepare] Pinned (params): {}", param_count); + eprintln!("[loopform/prepare] Carriers (locals): {}", carrier_count); + } + Ok(()) } @@ -276,11 +303,15 @@ impl LoopFormBuilder { /// - `latch_id`: The block that closes the canonical backedge to `header`. /// - `continue_snapshots`: Per-`continue` block variable snapshots. /// Each entry represents a predecessor of `header` created by `continue`. + /// - `writes`: Variables modified in loop body (Step 5-1: 選択肢2) + /// Used to distinguish true carriers from loop-invariant variables + /// (Currently unused - PHI optimization uses optimize_same_value() instead) pub fn seal_phis( &mut self, ops: &mut O, latch_id: BasicBlockId, continue_snapshots: &[(BasicBlockId, HashMap)], + _writes: &std::collections::HashSet, // Step 5-1/5-2: Reserved for future optimization ) -> Result<(), String> { let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok(); @@ -320,6 +351,20 @@ impl LoopFormBuilder { sanitize_phi_inputs(&mut inputs); + // Step 5-4: φ縮約(self-φ撲滅) + // 全入力が同じValueIdなら、PHI不要(loop-invariant) + if let Some(same_value) = LoopSnapshotMergeBox::optimize_same_value(&inputs) { + if debug { + eprintln!( + "[loopform/seal_phis] OPTIMIZED pinned '{}': phi={:?} → same_value={:?} (loop-invariant)", + pinned.name, pinned.header_phi, same_value + ); + } + // Skip PHI update - this variable is truly loop-invariant + // The header_phi will become dead code and can be cleaned up later + continue; + } + if debug { eprintln!( "[loopform/seal_phis] pinned '{}' phi={:?} inputs={:?}", @@ -356,6 +401,20 @@ impl LoopFormBuilder { sanitize_phi_inputs(&mut inputs); + // Step 5-4: φ縮約(self-φ撲滅) + // 全入力が同じValueIdなら、PHI不要(誤分類されたloop-invariant) + if let Some(same_value) = LoopSnapshotMergeBox::optimize_same_value(&inputs) { + if debug { + eprintln!( + "[loopform/seal_phis] OPTIMIZED carrier '{}': phi={:?} → same_value={:?} (misclassified as carrier, actually loop-invariant)", + carrier.name, carrier.header_phi, same_value + ); + } + // Skip PHI update - this variable was misclassified as carrier + // but is actually loop-invariant (no modifications in loop body) + continue; + } + if debug { eprintln!( "[loopform/seal_phis] carrier '{}' phi={:?} inputs={:?}", @@ -954,8 +1013,10 @@ mod tests { let continue_snapshots = vec![(cont_bb, cont_snapshot)]; // Act: seal PHIs + use std::collections::HashSet; + let writes = HashSet::new(); // Empty writes for test builder - .seal_phis(&mut ops, latch, &continue_snapshots) + .seal_phis(&mut ops, latch, &continue_snapshots, &writes) .expect("seal_phis should succeed"); // We expect PHI updates for both pinned (p) and carrier (i) diff --git a/src/runner/json_v0_bridge/lowering/loop_.rs b/src/runner/json_v0_bridge/lowering/loop_.rs index c16bdabe..a94b67ba 100644 --- a/src/runner/json_v0_bridge/lowering/loop_.rs +++ b/src/runner/json_v0_bridge/lowering/loop_.rs @@ -227,6 +227,19 @@ pub(super) fn lower_loop_stmt( // 1) preheader スナップショット(Env_in(loop)) let base_vars = vars.clone(); + + // DEBUG: Log preheader snapshot + if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() { + eprintln!("[loop_/lower] === PREHEADER SNAPSHOT (bb={:?}) ===", preheader_bb); + eprintln!("[loop_/lower] Function: {}", f.signature.name); + eprintln!("[loop_/lower] base_vars.len() = {}", base_vars.len()); + let mut sorted: Vec<_> = base_vars.iter().collect(); + sorted.sort_by_key(|(name, _)| name.as_str()); + for (name, value) in &sorted { + eprintln!("[loop_/lower] preheader var: {} = {:?}", name, value); + } + } + let mut block_var_maps: HashMap> = HashMap::new(); block_var_maps.insert(preheader_bb, base_vars.clone()); @@ -258,6 +271,32 @@ pub(super) fn lower_loop_stmt( let _ = super::pop_increment_hint(); let bend = bend_res?; + // Step 5-1: Writes集合収集(選択肢2+3統合: Snapshot比較で再代入検出) + // base_vars (preheader) と body_vars を比較し、ValueId が変わった変数を特定 + use std::collections::HashSet; + let mut writes = HashSet::new(); + for (name, &body_value) in &body_vars { + if let Some(&base_value) = base_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() { + let func_name = ops.f.signature.name.clone(); // Clone before borrowing + eprintln!("[loop_/lower] === WRITES COLLECTION (Step 5-1) ==="); + eprintln!("[loop_/lower] Function: {}", func_name); + eprintln!("[loop_/lower] {} variables modified in loop body", writes.len()); + let mut sorted_writes: Vec<_> = writes.iter().collect(); + sorted_writes.sort(); + for name in &sorted_writes { + eprintln!("[loop_/lower] WRITE: {}", name); + } + } + // スナップショット収集(Env_out(loop) 用) let continue_snaps = super::pop_continue_snapshots(); let exit_snaps = super::pop_exit_snapshots(); @@ -324,7 +363,8 @@ pub(super) fn lower_loop_stmt( }; // 7) header PHI seal(latch + canonical continue_merge スナップショット) - loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps)?; + // Step 5-1/5-2: Pass writes 集合 for PHI縮約 + loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps, &writes)?; // 8) exit PHI(header fallthrough + break スナップショット) // Option C: Create inspector (build_exit_phis will populate it)