Files
hakorune/src/mir/loop_builder.rs
nyash-codex c4d25e7773 feat(phi): __pin$変数の完全除外でValueId(313)→(300)に改善!
🎯 **Task先生の発見を実装**:
- __pin$ temporary変数をBodyLocalInternalとして強制分類
- Writes収集から__pin$変数を除外(carrier誤判定防止)

📦 **変更ファイル**:
- loop_var_classifier.rs: Priority 0で__pin$変数を自動BodyLocalInternal分類
- loop_builder.rs: Writes収集で__pin$除外
- loop_.rs (JSON): Writes収集で__pin$除外

 **テスト結果**: 267 PASS / 1 FAIL(新規ユニットテスト追加)
- test_classify_pin_temporary_variables: PASS 
- mir_funcscanner_skip_ws: ValueId(313)→(300)に改善(段階的進捗)

🔍 **ValueId未定義エラー改善履歴**:
- 最初: ValueId(313) at BasicBlockId(201)
- __pin$分類追加: ValueId(301) at BasicBlockId(210)
- Writes除外: ValueId(300) at BasicBlockId(207)
  → 着実に減少中!

📋 **完了ステップ**:
 Step 5-1: Writes集合収集(__pin$除外追加)
 Step 5-2: ValueId比較ロジック
 Step 5-4: φ縮約実装
 Step 5-5-A: PHI pred mismatch解決
 Step 5-5-B-partial: __pin$変数問題部分解決

🎯 **次のステップ**: ValueId(300)根本原因特定(Continue merge PHI実装検討)
2025-11-20 14:14:37 +09:00

1184 lines
51 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::control_form::{ControlForm, IfShape, LoopShape, is_control_form_trace_on};
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
use crate::ast::ASTNode;
use std::collections::HashMap;
// Phase 15 段階的根治戦略:制御フローユーティリティ
use super::utils::{
is_current_block_terminated,
capture_actual_predecessor_and_jump,
};
/// ループ脱出の種類(箱化・共通化のための型)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LoopExitKind {
/// break文exit blockへジャンプ
Break,
/// continue文header blockへジャンプ
Continue,
}
/// ループビルダー - SSA形式でのループ構築を管理
pub struct LoopBuilder<'a> {
/// 親のMIRビルダーへの参照
parent_builder: &'a mut super::builder::MirBuilder,
/// ブロックごとの変数マップ(スコープ管理)
#[allow(dead_code)]
block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>>,
/// ループヘッダーIDcontinue 先の既定値として使用)
loop_header: Option<BasicBlockId>,
/// continue 文がジャンプするターゲットブロック
/// - 既定: header と同一
/// - 将来: canonical continue merge ブロックに差し替えるためのフック
continue_target: 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
// =============================================================
// 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)
}
/// 【箱化】ループ脱出の共通処理break/continue統一化
///
/// Phase 25.1o: break と continue の共通パターンを抽出し、
/// LoopExitKind で振る舞いを切り替える統一メソッド。
///
/// # 処理フロー
/// 1. 現在の変数マップをスナップショット
/// 2. 適切なスナップショットリストに追加Break → exit_snapshots, Continue → continue_snapshots
/// 3. ターゲットブロックへジャンプBreak → exit, Continue → header
/// 4. unreachable ブロックに切り替え
fn do_loop_exit(&mut self, kind: LoopExitKind) -> Result<ValueId, String> {
// 1. スナップショット取得(共通処理)
let snapshot = self.get_current_variable_map();
let cur_block = self.current_block()?;
// 2. スナップショット保存kind別処理
match kind {
LoopExitKind::Break => {
if std::env::var("NYASH_LOOPFORM_DEBUG").ok().as_deref() == Some("1") {
eprintln!("[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
cur_block, snapshot.keys().collect::<Vec<_>>());
}
self.exit_snapshots.push((cur_block, snapshot));
}
LoopExitKind::Continue => {
self.block_var_maps.insert(cur_block, snapshot.clone());
self.continue_snapshots.push((cur_block, snapshot));
}
}
// 3. ターゲットブロックへジャンプkind別処理
match kind {
LoopExitKind::Break => {
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) {
self.jump_with_pred(exit_bb)?;
}
}
LoopExitKind::Continue => {
// 既定では header にジャンプするが、canonical continue merge を導入した場合は
// continue_target 側を優先する。
if let Some(target) = self.continue_target.or(self.loop_header) {
self.jump_with_pred(target)?;
}
}
}
// 4. unreachable ブロックに切り替え(共通処理)
self.switch_to_unreachable_block_with_void()
}
/// Handle a `break` statement: jump to loop exit and continue in a fresh unreachable block.
/// 【箱化】do_loop_exit() への thin wrapper
fn do_break(&mut self) -> Result<ValueId, String> {
self.do_loop_exit(LoopExitKind::Break)
}
/// Handle a `continue` statement: snapshot vars, jump to loop header, then continue in a fresh unreachable block.
/// 【箱化】do_loop_exit() への thin wrapper
fn do_continue(&mut self) -> Result<ValueId, String> {
self.do_loop_exit(LoopExitKind::Continue)
}
// =============================================================
// Lifecycle — create builder, main loop construction
// =============================================================
/// 新しいループビルダーを作成
pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self {
Self {
parent_builder: parent,
block_var_maps: HashMap::new(),
loop_header: None,
continue_target: None,
continue_snapshots: Vec::new(),
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
}
}
/// SSA形式でループを構築 (LoopForm v2 only)
pub fn build_loop(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
// 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<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);
}
}
// 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() {
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);
// Phase 26-A-4: ValueIdベース判定に変更名前ベース → 型安全)
let is_param = self.is_parameter(*value);
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)?;
// 📦 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)?;
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);
// 既定の continue 先を canonical continue_merge ブロックにする。
// ここを差し替えることで do_loop_exit(Continue) のターゲットを一元化する。
self.continue_target = Some(continue_merge_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)?;
// 📦 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() {
eprintln!("[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() {
eprintln!("[loopform/condition] AFTER emit_branch: current_block={:?}", self.current_block()?);
eprintln!("[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) {
eprintln!("[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() {
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)?;
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 (LoopVarClassBox) 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: HashMap<String, ValueId> = 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: HashMap<String, Vec<(BasicBlockId, ValueId)>> = HashMap::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 ードを生成LoopSnapshotMergeBox の最適化を使用)
let mut merged = HashMap::new();
for (var_name, mut inputs) in all_vars {
// Phase 25.2: optimize_same_value() で最適化判定
let result_value = if let Some(same_val) = LoopSnapshotMergeBox::optimize_same_value(&inputs) {
// 全て同じ値 or 単一入力 → PHI 不要
same_val
} else {
// 異なる値を持つ場合は PHI ノードを生成
// Phase 25.2: sanitize_inputs() で入力を正規化
LoopSnapshotMergeBox::sanitize_inputs(&mut inputs);
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,
});
}
}
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 {
HashMap::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 を呼ぶ
let continue_snaps: Vec<(BasicBlockId, HashMap<String, ValueId>)> =
if merged_snapshot.is_empty() {
vec![]
} else {
vec![(continue_merge_id, merged_snapshot.clone())]
};
// 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)
// 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 のターゲットブロックをユニーク化して収集
let mut break_set: HashSet<BasicBlockId> = HashSet::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<BasicBlockId> = if self.continue_snapshots.is_empty() {
Vec::new()
} else {
vec![continue_merge_id]
};
let break_targets: Vec<BasicBlockId> = 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 のときだけ、StageB 周辺ループの
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
// Build exit PHIs for break statements using ControlForm wrapper
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)
}
// =============================================================
// PHI Helpers — prepare/finalize PHIs and block sealing
// =============================================================
/// ループ変数の準備(事前検出または遅延生成)
///
/// ポリシー:
/// - ループキャリア(ループ本体で再代入される変数)と pinned 変数のみを PHI 対象とする。
/// - ループ不変のローカルtext_len / pattern_len など)は preheader 値をそのまま使い、
/// 不要な PHI を張らないことで SSA 破綻(同一 ValueId の二重定義)を防ぐ。
/// ブロックをシールし、不完全なPhi nodeを完成させる
/// Exitブロックで変数のPHIを生成breakポイントでの値を統一
// --- ヘルパーメソッド(親ビルダーへの委譲) ---
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_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())
}
}
// =============================================================
// 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_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();
// Phase 25.1h: ControlForm統合版に切り替え
let if_shape = IfShape {
cond_block: pre_branch_bb,
then_block: then_bb,
else_block: Some(else_bb),
merge_block: merge_bb,
};
let form = ControlForm::from_if(if_shape.clone());
// Region/GC 観測レイヤPhase 25.1l:
// NYASH_REGION_TRACE=1 のときだけ、StageB 周辺 If 構造の
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
let mut ops = Ops(self);
crate::mir::phi_core::if_phi::merge_modified_with_control(
&mut ops,
&form,
&pre_if_var_map,
&then_var_map_end,
&else_var_map_end_opt,
None,
then_pred_to_merge,
else_pred_to_merge,
)?;
// ControlForm 観測: 環境フラグ未設定時は既定ONのとき IfShape をダンプ
if is_control_form_trace_on() {
form.debug_dump();
#[cfg(debug_assertions)]
if let Some(ref func) = self.parent_builder.current_function {
if_shape.debug_validate(func);
}
}
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 get_block_predecessors(&self, block: BasicBlockId) -> std::collections::HashSet<BasicBlockId> {
// 📦 Hotfix 6: Get actual CFG predecessors for PHI validation
if let Some(ref func) = self.parent_builder.current_function {
if let Some(bb) = func.blocks.get(&block) {
bb.predecessors.clone()
} else {
std::collections::HashSet::new() // Non-existent blocks have no predecessors
}
} else {
std::collections::HashSet::new()
}
}
/// Phase 26-A-4: ValueIdベースのパラメータ判定GUARD Bug Prevention
///
/// 旧実装(名前ベース)の問題点:
/// - ValueId(0) を「常に未初期化」と誤判定
/// - パラメータ s=ValueId(0) も弾いてしまうGUARDバグ
///
/// 新実装(型ベース)の利点:
/// - MirValueKindで型安全判定
/// - ValueId(0)でもParameter(0)なら正しく判定
fn is_parameter(&self, value_id: ValueId) -> bool {
// Phase 26-A-4: 型安全なパラメータ判定を使用
// parent_builder.is_value_parameter() は Phase 26-A-2 で実装済み
let is_param = self.parent_builder.is_value_parameter(value_id);
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[is_parameter] ValueId({}) -> {} (kind = {:?})",
value_id.0, is_param, self.parent_builder.get_value_kind(value_id));
}
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)
}
}