diff --git a/src/mir/loop_builder.rs b/src/mir/loop_builder.rs index 7a30ff4a..50993817 100644 --- a/src/mir/loop_builder.rs +++ b/src/mir/loop_builder.rs @@ -148,30 +148,14 @@ impl<'a> LoopBuilder<'a> { } } - /// SSA形式でループを構築 (Feature flag dispatch) + /// SSA形式でループを構築 (LoopForm v2 only) pub fn build_loop( &mut self, condition: ASTNode, body: Vec, ) -> Result { - // Check feature flag for LoopForm PHI v2 - // 📦 Default to true (v2 is now stable and default) - // Set NYASH_LOOPFORM_PHI_V2=0 to use legacy version if needed - let use_loopform_v2 = std::env::var("NYASH_LOOPFORM_PHI_V2") - .map(|v| v == "1" || v.to_lowercase() == "true") - .unwrap_or(true); - - 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! LoopForm v2 is now default."); - eprintln!("⚠️ Legacy version may be deprecated in future releases."); - self.build_loop_legacy(condition, body) - } + // Phase 7-F: Legacy loop builder removed - LoopForm v2 is now the only implementation + self.build_loop_with_loopform(condition, body) } /// SSA形式でループを構築 (LoopFormBuilder implementation) @@ -421,276 +405,6 @@ impl<'a> LoopBuilder<'a> { 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