Files
hakorune/src/mir/loop_builder.rs
nyash-codex f74b7d2b04 📦 Hotfix 1 & 2: Parameter ValueId Reservation + Exit PHI Validation (Box-First Theory)
**箱理論に基づく根治的修正**:

## 🎯 Hotfix 1: Parameter ValueId Reservation (パラメータ ValueId 予約)

### 根本原因
- MirFunction counter が params.len() を考慮していなかった
- local variables が parameter ValueIds を上書き

### 箱理論的解決
1. **LoopFormContext Box**
   - パラメータ予約を明示的に管理
   - 境界をはっきりさせる

2. **MirFunction::new() 改善**
   - `initial_counter = param_count.max(1)` でパラメータ予約
   - Parameters are %0, %1, ..., %N-1

3. **ensure_counter_after() 強化**
   - パラメータ数 + 既存 ValueIds 両方を考慮
   - `min_counter = param_count.max(max_id + 1)`

4. **reserve_parameter_value_ids() 追加**
   - 明示的な予約メソッド(Box-First)

## 🎯 Hotfix 2: Exit PHI Predecessor Validation (Exit PHI 検証)

### 根本原因
- LoopForm builder が存在しないブロックを PHI predecessor に追加
- 「幽霊ブロック」問題

### 箱理論的解決
1. **LoopFormOps.block_exists() 追加**
   - CFG 存在確認メソッド
   - 境界を明確化

2. **build_exit_phis() 検証**
   - 非存在ブロックをスキップ
   - デバッグログ付き

### 実装ファイル
- `src/mir/function.rs`: Parameter reservation
- `src/mir/phi_core/loopform_builder.rs`: Context + validation
- `src/mir/loop_builder.rs`: LoopFormOps impl
- `src/mir/builder/stmts.rs`: Local variable allocation

### 業界標準準拠
-  LLVM IR: Parameters are %0, %1, ...
-  SSA Form: PHI predecessors must exist in CFG
-  Cytron et al. (1991): Parameter reservation principle

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:39:45 +09:00

1328 lines
56 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::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<BasicBlockId, Vec<IncompletePhi>>,
/// ブロックごとの変数マップ(スコープ管理)
#[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
fn find_copy_source(&self, block_id: BasicBlockId, dst: ValueId) -> Option<ValueId> {
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<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)
}
/// Handle a `break` statement: jump to loop exit and continue in a fresh unreachable block.
fn do_break(&mut self) -> Result<ValueId, String> {
// 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::<Vec<_>>());
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<ValueId, String> {
// 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<ASTNode>,
) -> Result<ValueId, String> {
// 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<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
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 &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)?;
// 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<ASTNode>,
) -> Result<ValueId, String> {
// 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<String> = 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<String> = 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<String, ValueId>,
assigned_vars: &[String],
) -> Result<Vec<crate::mir::phi_core::loop_phi::IncompletePhi>, 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,
&current_vars,
);
let incs = crate::mir::phi_core::loop_phi::prepare_loop_variables_with(
self,
header_id,
preheader_id,
&current_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<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_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<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_expression_with_phis(&mut self, expr: ASTNode) -> Result<ValueId, String> {
// Phi nodeの結果を考慮しながら式を構築
self.parent_builder.build_expression(expr)
}
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();
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<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 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)
}
}