2025-08-18 23:36:40 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* MIR Loop Builder - SSA形式でのループ構築専用モジュール
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-11-21 06:22:21 +09:00
|
|
|
|
* Sealed/Unsealed blockとPhi nodeを使った正しいループ実装。
|
|
|
|
|
|
*
|
|
|
|
|
|
* LoopForm v2 の「形」をここで固定している:
|
|
|
|
|
|
* - preheader: ループに入る直前のブロック(初期値の copy 発生源)
|
|
|
|
|
|
* - header : ループ条件を評価するブロック(`loop(cond)` の `cond` 部分)
|
|
|
|
|
|
* - body : ループ本体(ユーザーコードが書いたブロック)
|
|
|
|
|
|
* - latch : body の末尾から header へ戻る backedge 用ブロック
|
|
|
|
|
|
* - exit : ループ脱出先(`break` / `cond == false` が合流するブロック)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 典型パターン(ControlForm::LoopShape):
|
|
|
|
|
|
* - Case A: header-cond + header→exit + body→exit(`loop(i < n) { if (...) break }`)
|
|
|
|
|
|
* - Case B: constant-true + body→exit のみ(`loop(1 == 1) { if (...) break }`)
|
|
|
|
|
|
* - この場合、header→exit のエッジは存在しないので、exit PHI に header 値を入れてはいけない。
|
|
|
|
|
|
* - Case C: continue_merge を経由して header に戻る経路あり(`continue` を含むループ)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* それぞれのケースは ControlForm / LoopSnapshotMergeBox / ExitPhiBuilder に伝搬され、
|
|
|
|
|
|
* exit PHI の入力選択や BodyLocalInternal 変数の扱いに反映される。
|
2025-08-18 23:36:40 +09:00
|
|
|
|
*/
|
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
2025-11-21 06:22:21 +09:00
|
|
|
|
use crate::ast::ASTNode;
|
|
|
|
|
|
use crate::mir::control_form::{is_control_form_trace_on, ControlForm, IfShape, LoopShape};
|
2025-11-20 01:58:40 +09:00
|
|
|
|
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
2025-11-21 06:22:21 +09:00
|
|
|
|
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
2025-11-20 18:07:11 +09:00
|
|
|
|
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
|
2025-11-22 05:33:40 +09:00
|
|
|
|
use std::collections::{BTreeMap, BTreeSet}; // Phase 25.1: 決定性確保
|
2025-08-18 23:36:40 +09:00
|
|
|
|
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// Phase 15 段階的根治戦略:制御フローユーティリティ
|
2025-11-21 06:22:21 +09:00
|
|
|
|
use super::utils::{capture_actual_predecessor_and_jump, is_current_block_terminated};
|
2025-09-23 07:13:32 +09:00
|
|
|
|
|
2025-11-19 08:56:44 +09:00
|
|
|
|
/// ループ脱出の種類(箱化・共通化のための型)
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
enum LoopExitKind {
|
|
|
|
|
|
/// break文(exit blockへジャンプ)
|
|
|
|
|
|
Break,
|
|
|
|
|
|
/// continue文(header blockへジャンプ)
|
|
|
|
|
|
Continue,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ループビルダー - SSA形式でのループ構築を管理
|
|
|
|
|
|
pub struct LoopBuilder<'a> {
|
|
|
|
|
|
/// 親のMIRビルダーへの参照
|
|
|
|
|
|
parent_builder: &'a mut super::builder::MirBuilder,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ブロックごとの変数マップ(スコープ管理)
|
2025-11-22 05:33:40 +09:00
|
|
|
|
/// Phase 25.1: BTreeMap → BTreeMap(決定性確保)
|
2025-08-21 12:14:33 +09:00
|
|
|
|
#[allow(dead_code)]
|
2025-11-22 05:33:40 +09:00
|
|
|
|
block_var_maps: BTreeMap<BasicBlockId, BTreeMap<String, ValueId>>,
|
2025-09-10 17:39:46 +09:00
|
|
|
|
|
2025-11-20 01:41:17 +09:00
|
|
|
|
/// ループヘッダーID(continue 先の既定値として使用)
|
2025-09-10 17:39:46 +09:00
|
|
|
|
loop_header: Option<BasicBlockId>,
|
|
|
|
|
|
|
2025-11-20 01:41:17 +09:00
|
|
|
|
/// continue 文がジャンプするターゲットブロック
|
|
|
|
|
|
/// - 既定: header と同一
|
|
|
|
|
|
/// - 将来: canonical continue merge ブロックに差し替えるためのフック
|
|
|
|
|
|
continue_target: Option<BasicBlockId>,
|
|
|
|
|
|
|
2025-09-10 17:39:46 +09:00
|
|
|
|
/// continue文からの変数スナップショット
|
2025-11-22 05:33:40 +09:00
|
|
|
|
continue_snapshots: Vec<(BasicBlockId, BTreeMap<String, ValueId>)>,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
|
2025-09-23 09:48:29 +09:00
|
|
|
|
/// break文からの変数スナップショット(exit PHI生成用)
|
2025-11-22 05:33:40 +09:00
|
|
|
|
exit_snapshots: Vec<(BasicBlockId, BTreeMap<String, ValueId>)>,
|
2025-09-23 07:25:58 +09:00
|
|
|
|
// フェーズM: no_phi_modeフィールド削除(常にPHI使用)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> LoopBuilder<'a> {
|
2025-09-25 10:23:14 +09:00
|
|
|
|
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
|
2025-11-17 06:31:31 +09:00
|
|
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// Control Helpers — break/continue/jumps/unreachable handling
|
|
|
|
|
|
// =============================================================
|
2025-09-19 02:07:38 +09:00
|
|
|
|
|
|
|
|
|
|
/// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-21 06:22:21 +09:00
|
|
|
|
/// [LoopForm] 【箱化】ループ脱出の共通処理(break/continue統一化)
|
2025-11-19 08:56:44 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// Phase 25.1o: break と continue の共通パターンを抽出し、
|
|
|
|
|
|
/// LoopExitKind で振る舞いを切り替える統一メソッド。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # 処理フロー
|
|
|
|
|
|
/// 1. 現在の変数マップをスナップショット
|
2025-11-21 06:22:21 +09:00
|
|
|
|
/// 2. [LoopForm] スナップショット保存(Break → exit_snapshots, Continue → continue_snapshots)
|
|
|
|
|
|
/// 3. [LoopForm] ターゲットブロックへジャンプ(Break → exit, Continue → header/continue_merge)
|
2025-11-19 08:56:44 +09:00
|
|
|
|
/// 4. unreachable ブロックに切り替え
|
|
|
|
|
|
fn do_loop_exit(&mut self, kind: LoopExitKind) -> Result<ValueId, String> {
|
|
|
|
|
|
// 1. スナップショット取得(共通処理)
|
2025-09-23 09:48:29 +09:00
|
|
|
|
let snapshot = self.get_current_variable_map();
|
|
|
|
|
|
let cur_block = self.current_block()?;
|
|
|
|
|
|
|
2025-11-21 06:22:21 +09:00
|
|
|
|
// 2. [LoopForm] exit-break path: スナップショット保存(exit PHI入力用)
|
|
|
|
|
|
// [LoopForm] continue-backedge path: スナップショット保存(continue_merge → header)
|
2025-11-19 08:56:44 +09:00
|
|
|
|
match kind {
|
|
|
|
|
|
LoopExitKind::Break => {
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").ok().as_deref() == Some("1") {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG/do_break] Saved snapshot from block {:?}, vars: {:?}",
|
|
|
|
|
|
cur_block,
|
|
|
|
|
|
snapshot.keys().collect::<Vec<_>>()
|
|
|
|
|
|
);
|
2025-11-19 08:56:44 +09:00
|
|
|
|
}
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
2025-09-19 02:07:38 +09:00
|
|
|
|
}
|
2025-11-19 08:56:44 +09:00
|
|
|
|
|
|
|
|
|
|
// 3. ターゲットブロックへジャンプ(kind別処理)
|
|
|
|
|
|
match kind {
|
|
|
|
|
|
LoopExitKind::Break => {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder)
|
|
|
|
|
|
{
|
2025-11-19 08:56:44 +09:00
|
|
|
|
self.jump_with_pred(exit_bb)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
LoopExitKind::Continue => {
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// 既定では header にジャンプするが、canonical continue merge を導入した場合は
|
|
|
|
|
|
// continue_target 側を優先する。
|
|
|
|
|
|
if let Some(target) = self.continue_target.or(self.loop_header) {
|
|
|
|
|
|
self.jump_with_pred(target)?;
|
2025-11-19 08:56:44 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. unreachable ブロックに切り替え(共通処理)
|
2025-09-19 02:07:38 +09:00
|
|
|
|
self.switch_to_unreachable_block_with_void()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-19 08:56:44 +09:00
|
|
|
|
/// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-19 02:07:38 +09:00
|
|
|
|
/// Handle a `continue` statement: snapshot vars, jump to loop header, then continue in a fresh unreachable block.
|
2025-11-19 08:56:44 +09:00
|
|
|
|
/// 【箱化】do_loop_exit() への thin wrapper
|
2025-09-19 02:07:38 +09:00
|
|
|
|
fn do_continue(&mut self) -> Result<ValueId, String> {
|
2025-11-19 08:56:44 +09:00
|
|
|
|
self.do_loop_exit(LoopExitKind::Continue)
|
2025-09-19 02:07:38 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// Lifecycle — create builder, main loop construction
|
|
|
|
|
|
// =============================================================
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// 新しいループビルダーを作成
|
|
|
|
|
|
pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
parent_builder: parent,
|
2025-11-22 05:33:40 +09:00
|
|
|
|
block_var_maps: BTreeMap::new(),
|
2025-09-10 17:39:46 +09:00
|
|
|
|
loop_header: None,
|
2025-11-20 01:41:17 +09:00
|
|
|
|
continue_target: None,
|
2025-09-10 17:39:46 +09:00
|
|
|
|
continue_snapshots: Vec::new(),
|
2025-11-21 06:22:21 +09:00
|
|
|
|
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-18 19:32:05 +09:00
|
|
|
|
/// SSA形式でループを構築 (LoopForm v2 only)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
pub fn build_loop(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
2025-11-17 05:24:07 +09:00
|
|
|
|
) -> Result<ValueId, String> {
|
2025-11-18 19:32:05 +09:00
|
|
|
|
// Phase 7-F: Legacy loop builder removed - LoopForm v2 is now the only implementation
|
|
|
|
|
|
self.build_loop_with_loopform(condition, body)
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// SSA形式でループを構築 (LoopFormBuilder implementation)
|
|
|
|
|
|
fn build_loop_with_loopform(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-11-18 06:39:45 +09:00
|
|
|
|
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 {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
eprintln!("[build_loop_with_loopform] condition={:?}", condition);
|
|
|
|
|
|
eprintln!("[build_loop_with_loopform] body.len()={}", body.len());
|
|
|
|
|
|
}
|
2025-11-17 05:48:03 +09:00
|
|
|
|
// 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();
|
|
|
|
|
|
|
2025-11-18 06:39:45 +09:00
|
|
|
|
// DEBUG: Show variable map before guard check
|
2025-11-17 05:48:03 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] before_loop_id={:?}, variable_map size={}",
|
|
|
|
|
|
before_loop_id,
|
|
|
|
|
|
current_vars.len()
|
|
|
|
|
|
);
|
2025-11-17 05:48:03 +09:00
|
|
|
|
for (name, value) in ¤t_vars {
|
|
|
|
|
|
eprintln!(" {} -> {:?}", name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 08:41:37 +09:00
|
|
|
|
// 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.
|
2025-11-18 06:39:45 +09:00
|
|
|
|
|
2025-11-17 05:48:03 +09:00
|
|
|
|
let preheader_id = self.new_block();
|
2025-11-17 05:24:07 +09:00
|
|
|
|
let header_id = self.new_block();
|
|
|
|
|
|
let body_id = self.new_block();
|
|
|
|
|
|
let latch_id = self.new_block();
|
|
|
|
|
|
let exit_id = self.new_block();
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// Phase 25.1q: canonical continue merge block
|
|
|
|
|
|
// All continue 文は一度このブロックに集約してから header へ戻る。
|
|
|
|
|
|
let continue_merge_id = self.new_block();
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
2025-11-17 05:48:03 +09:00
|
|
|
|
// Jump from current block to preheader
|
2025-11-18 09:26:39 +09:00
|
|
|
|
let entry_block = self.current_block()?;
|
2025-11-17 05:48:03 +09:00
|
|
|
|
self.emit_jump(preheader_id)?;
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
|
2025-11-21 06:22:21 +09:00
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
preheader_id,
|
|
|
|
|
|
entry_block,
|
|
|
|
|
|
)?;
|
2025-11-17 05:48:03 +09:00
|
|
|
|
|
2025-11-17 05:24:07 +09:00
|
|
|
|
// 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() {
|
2025-11-17 05:48:03 +09:00
|
|
|
|
eprintln!("[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}",
|
|
|
|
|
|
preheader_id, header_id, body_id, latch_id, exit_id);
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] variable_map at loop entry (size={}):",
|
|
|
|
|
|
current_vars.len()
|
|
|
|
|
|
);
|
2025-11-17 05:48:03 +09:00
|
|
|
|
let mut loop_count = 0;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
for (name, value) in ¤t_vars {
|
2025-11-17 05:48:03 +09:00
|
|
|
|
loop_count += 1;
|
|
|
|
|
|
eprintln!(" [{}] {} -> {:?}", loop_count, name, value);
|
2025-11-20 09:49:13 +09:00
|
|
|
|
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
|
|
|
|
|
let is_param = self.is_parameter(*value);
|
2025-11-17 05:48:03 +09:00
|
|
|
|
eprintln!(" param={}", is_param);
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
2025-11-17 05:48:03 +09:00
|
|
|
|
eprintln!("[loopform] iterated {} times", loop_count);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
eprintln!("[loopform] BEFORE prepare_structure: current_function=None");
|
|
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
loopform.prepare_structure(self, ¤t_vars)?;
|
2025-11-18 06:39:45 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
eprintln!("[loopform] AFTER prepare_structure: current_function=None");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
// Pass 2: Emit preheader (copies and jump to header)
|
|
|
|
|
|
loopform.emit_preheader(self)?;
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// 📦 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)?;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
// 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);
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// 既定の continue 先を canonical continue_merge ブロックにする。
|
|
|
|
|
|
// ここを差し替えることで do_loop_exit(Continue) のターゲットを一元化する。
|
|
|
|
|
|
self.continue_target = Some(continue_merge_id);
|
2025-11-17 05:24:07 +09:00
|
|
|
|
self.continue_snapshots.clear();
|
|
|
|
|
|
self.exit_snapshots.clear();
|
|
|
|
|
|
|
2025-11-21 06:22:21 +09:00
|
|
|
|
// [LoopForm] header-cond: cond true → body, false → exit (Case A/B)
|
|
|
|
|
|
// - Case A: loop(i < n) → header can branch to exit directly
|
|
|
|
|
|
// - Case B: loop(1 == 1) → header always enters body, exit only via break
|
2025-11-18 04:26:50 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE build_expression: current_block={:?}",
|
|
|
|
|
|
self.current_block()?
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
}
|
2025-11-18 04:26:50 +09:00
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
let cond_value = self.parent_builder.build_expression(condition)?;
|
2025-11-18 04:26:50 +09:00
|
|
|
|
// 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() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] AFTER build_expression: branch_source_block={:?}",
|
|
|
|
|
|
branch_source_block
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
}
|
2025-11-18 04:26:50 +09:00
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
self.emit_branch(cond_value, body_id, exit_id)?;
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// 📦 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() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}",
|
|
|
|
|
|
exit_id, branch_source_block
|
|
|
|
|
|
);
|
2025-11-18 09:26:39 +09:00
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
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,
|
|
|
|
|
|
)?;
|
2025-11-18 04:26:50 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
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
|
|
|
|
|
|
);
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// Verify predecessors were added
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(exit_block) = func.blocks.get(&exit_id) {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] exit_block.predecessors = {:?}",
|
|
|
|
|
|
exit_block.predecessors
|
|
|
|
|
|
);
|
2025-11-18 09:26:39 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-18 04:26:50 +09:00
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
2025-11-20 13:26:57 +09:00
|
|
|
|
// 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 {
|
2025-11-20 14:14:37 +09:00
|
|
|
|
// Skip __pin$ temporary variables - they are always BodyLocalInternal
|
|
|
|
|
|
// (Task先生の発見: これらをcarrier扱いすると未定義ValueIdエラーの原因になる)
|
|
|
|
|
|
if name.starts_with("__pin$") && name.contains("$@") {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:26:57 +09:00
|
|
|
|
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) ===");
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/writes] {} variables modified in loop body",
|
|
|
|
|
|
writes.len()
|
|
|
|
|
|
);
|
2025-11-20 13:26:57 +09:00
|
|
|
|
let mut sorted_writes: Vec<_> = writes.iter().collect();
|
|
|
|
|
|
sorted_writes.sort();
|
|
|
|
|
|
for name in &sorted_writes {
|
|
|
|
|
|
eprintln!("[loopform/writes] WRITE: {}", name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 05:24:07 +09:00
|
|
|
|
// 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
|
2025-11-19 23:12:01 +09:00
|
|
|
|
for (name, value) in &body_end_vars {
|
|
|
|
|
|
self.update_variable(name.clone(), *value);
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// 📦 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)?;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
2025-11-19 23:12:01 +09:00
|
|
|
|
// Phase 25.1c/k: body-local 変数の PHI 生成
|
|
|
|
|
|
// BreakFinderBox / FuncScannerBox 等で、loop body 内で新規宣言された local 変数が
|
|
|
|
|
|
// loop header に戻った時に undefined になる問題を修正
|
2025-11-20 13:49:55 +09:00
|
|
|
|
//
|
2025-11-20 13:55:24 +09:00
|
|
|
|
// 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
|
2025-11-19 23:12:01 +09:00
|
|
|
|
let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1");
|
2025-11-20 13:49:55 +09:00
|
|
|
|
|
2025-11-20 13:55:24 +09:00
|
|
|
|
// 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)
|
2025-11-21 06:22:21 +09:00
|
|
|
|
if false { // Disabled for Step 5-5-B experiment
|
|
|
|
|
|
// [Original code removed - see git history if needed]
|
2025-11-19 23:12:01 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// 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 を作る)
|
2025-11-20 01:58:40 +09:00
|
|
|
|
// Phase 25.2: LoopSnapshotMergeBox を使って整理
|
2025-11-20 01:41:17 +09:00
|
|
|
|
self.set_current_block(continue_merge_id)?;
|
|
|
|
|
|
|
2025-11-22 05:33:40 +09:00
|
|
|
|
let merged_snapshot: BTreeMap<String, ValueId> = if !raw_continue_snaps.is_empty() {
|
2025-11-20 01:41:17 +09:00
|
|
|
|
if trace_loop_phi {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Generating PHI nodes for {} continue paths",
|
|
|
|
|
|
raw_continue_snaps.len()
|
|
|
|
|
|
);
|
2025-11-20 01:41:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// すべての continue snapshot に現れる変数を収集
|
2025-11-22 05:33:40 +09:00
|
|
|
|
let mut all_vars: BTreeMap<String, Vec<(BasicBlockId, ValueId)>> = BTreeMap::new();
|
2025-11-20 01:41:17 +09:00
|
|
|
|
for (continue_bb, snapshot) in &raw_continue_snaps {
|
|
|
|
|
|
for (var_name, &value) in snapshot {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
all_vars
|
|
|
|
|
|
.entry(var_name.clone())
|
2025-11-20 01:41:17 +09:00
|
|
|
|
.or_default()
|
|
|
|
|
|
.push((*continue_bb, value));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 18:07:11 +09:00
|
|
|
|
// 各変数について PHI ノードを生成(Phase 26-B-3: PhiInputCollector使用)
|
2025-11-22 05:33:40 +09:00
|
|
|
|
let mut merged = BTreeMap::new();
|
2025-11-20 18:07:11 +09:00
|
|
|
|
for (var_name, inputs) in all_vars {
|
|
|
|
|
|
// Phase 26-B-3: Use PhiInputCollector
|
|
|
|
|
|
let mut collector = PhiInputCollector::new();
|
|
|
|
|
|
collector.add_snapshot(&inputs);
|
|
|
|
|
|
collector.sanitize();
|
|
|
|
|
|
|
2025-11-20 01:58:40 +09:00
|
|
|
|
// Phase 25.2: optimize_same_value() で最適化判定
|
2025-11-20 18:07:11 +09:00
|
|
|
|
let result_value = if let Some(same_val) = collector.optimize_same_value() {
|
2025-11-20 01:58:40 +09:00
|
|
|
|
// 全て同じ値 or 単一入力 → PHI 不要
|
|
|
|
|
|
same_val
|
2025-11-20 01:41:17 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
// 異なる値を持つ場合は PHI ノードを生成
|
2025-11-20 18:07:11 +09:00
|
|
|
|
let final_inputs = collector.finalize();
|
2025-11-20 01:58:40 +09:00
|
|
|
|
|
2025-11-20 01:41:17 +09:00
|
|
|
|
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,
|
2025-11-20 18:07:11 +09:00
|
|
|
|
inputs: final_inputs,
|
2025-11-20 01:41:17 +09:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if trace_loop_phi {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Generated PHI for '{}': {:?}",
|
|
|
|
|
|
var_name, phi_id
|
|
|
|
|
|
);
|
2025-11-20 01:41:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
phi_id
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-20 01:58:40 +09:00
|
|
|
|
merged.insert(var_name, result_value);
|
2025-11-20 01:41:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Note: 変数マップへの反映は seal_phis に委譲(干渉を避ける)
|
|
|
|
|
|
|
|
|
|
|
|
if trace_loop_phi {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Merged {} variables from {} paths",
|
|
|
|
|
|
merged.len(),
|
|
|
|
|
|
raw_continue_snaps.len()
|
|
|
|
|
|
);
|
2025-11-20 01:41:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
merged
|
|
|
|
|
|
} else {
|
2025-11-22 05:33:40 +09:00
|
|
|
|
BTreeMap::new()
|
2025-11-20 01:41:17 +09:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
2025-11-21 06:22:21 +09:00
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
continue_merge_id,
|
|
|
|
|
|
)?;
|
2025-11-20 01:41:17 +09:00
|
|
|
|
|
|
|
|
|
|
// Step 2: merged_snapshot を使って seal_phis を呼ぶ
|
2025-11-20 14:56:26 +09:00
|
|
|
|
// Phase 25.3: Continue merge PHI実装(Task先生の発見!)
|
|
|
|
|
|
// - continueが無いループでも、Latchブロックの値をHeader PHIに伝播する必要がある
|
|
|
|
|
|
// - これにより、Exit PHIがHeader PHI経由で正しい値を受け取れる
|
2025-11-22 05:33:40 +09:00
|
|
|
|
let continue_snaps: Vec<(BasicBlockId, BTreeMap<String, ValueId>)> = {
|
2025-11-20 14:56:26 +09:00
|
|
|
|
// まず、merged_snapshot(continue merge PHI結果)を追加
|
|
|
|
|
|
let mut snaps = if !merged_snapshot.is_empty() {
|
2025-11-20 01:41:17 +09:00
|
|
|
|
vec![(continue_merge_id, merged_snapshot.clone())]
|
2025-11-20 14:56:26 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
vec![]
|
2025-11-20 01:41:17 +09:00
|
|
|
|
};
|
2025-11-20 14:56:26 +09:00
|
|
|
|
|
|
|
|
|
|
// continueが無い場合でも、Latchブロックのスナップショットを追加
|
|
|
|
|
|
// これにより、seal_phis()がLatchからの値をHeader PHIに正しく接続できる
|
|
|
|
|
|
if raw_continue_snaps.is_empty() {
|
|
|
|
|
|
// continue文が無い場合、Latchブロックの現在の変数マップをキャプチャ
|
|
|
|
|
|
// Note: このタイミングでは current_block == exit_id だが、
|
|
|
|
|
|
// variable_map はLatch実行後の状態を保持している
|
|
|
|
|
|
let latch_snapshot = self.get_current_variable_map();
|
|
|
|
|
|
snaps.push((actual_latch_id, latch_snapshot));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
snaps
|
|
|
|
|
|
};
|
2025-11-20 13:26:57 +09:00
|
|
|
|
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
|
|
|
|
|
loopform.seal_phis(self, actual_latch_id, &continue_snaps, &writes)?;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// Step 3: seal body-local PHIs (complete the inputs)
|
2025-11-20 13:49:55 +09:00
|
|
|
|
// 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!
|
2025-11-19 23:12:01 +09:00
|
|
|
|
|
2025-11-17 05:24:07 +09:00
|
|
|
|
// Exit block
|
|
|
|
|
|
self.set_current_block(exit_id)?;
|
|
|
|
|
|
|
2025-11-18 19:21:26 +09:00
|
|
|
|
// Phase 25.1h: ControlForm統合版に切り替え
|
|
|
|
|
|
// continue / break のターゲットブロックをユニーク化して収集
|
2025-11-22 05:33:40 +09:00
|
|
|
|
// Phase 25.1: HashSet → BTreeSet(決定性確保)
|
|
|
|
|
|
let mut break_set: BTreeSet<BasicBlockId> = BTreeSet::new();
|
2025-11-18 19:21:26 +09:00
|
|
|
|
for (bb, _) in &self.exit_snapshots {
|
|
|
|
|
|
break_set.insert(*bb);
|
|
|
|
|
|
}
|
2025-11-20 01:41:17 +09:00
|
|
|
|
// 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]
|
|
|
|
|
|
};
|
2025-11-18 19:21:26 +09:00
|
|
|
|
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());
|
2025-11-18 19:05:46 +09:00
|
|
|
|
|
2025-11-19 02:44:40 +09:00
|
|
|
|
// Region/GC 観測レイヤ(Phase 25.1l):
|
|
|
|
|
|
// NYASH_REGION_TRACE=1 のときだけ、Stage‑B 周辺ループの
|
|
|
|
|
|
// Region 情報(entry/exit/slots)をログに出すよ。
|
|
|
|
|
|
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
|
|
|
|
|
|
|
2025-11-21 06:22:21 +09:00
|
|
|
|
// [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds)
|
|
|
|
|
|
// - Case A: header+break → exit PHI includes both paths
|
|
|
|
|
|
// - Case B: break-only → exit PHI excludes header (not a predecessor)
|
2025-11-17 05:24:07 +09:00
|
|
|
|
let exit_snaps = self.exit_snapshots.clone();
|
2025-11-18 19:21:26 +09:00
|
|
|
|
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
|
|
|
|
|
|
&loopform,
|
|
|
|
|
|
self,
|
|
|
|
|
|
&form,
|
|
|
|
|
|
&exit_snaps,
|
|
|
|
|
|
branch_source_block,
|
|
|
|
|
|
)?;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
// Pop loop context
|
|
|
|
|
|
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
|
|
|
|
|
|
2025-11-18 18:56:35 +09:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 05:24:07 +09:00
|
|
|
|
// Return void value
|
|
|
|
|
|
let void_dst = self.new_value();
|
|
|
|
|
|
self.emit_const(void_dst, ConstValue::Void)?;
|
|
|
|
|
|
Ok(void_dst)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// PHI Helpers — prepare/finalize PHIs and block sealing
|
|
|
|
|
|
// =============================================================
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ループ変数の準備(事前検出または遅延生成)
|
2025-11-18 04:26:50 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// ポリシー:
|
|
|
|
|
|
/// - ループキャリア(ループ本体で再代入される変数)と pinned 変数のみを PHI 対象とする。
|
|
|
|
|
|
/// - ループ不変のローカル(text_len / pattern_len など)は preheader 値をそのまま使い、
|
|
|
|
|
|
/// 不要な PHI を張らないことで SSA 破綻(同一 ValueId の二重定義)を防ぐ。
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ブロックをシールし、不完全なPhi nodeを完成させる
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-23 09:48:29 +09:00
|
|
|
|
/// Exitブロックで変数のPHIを生成(breakポイントでの値を統一)
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// --- ヘルパーメソッド(親ビルダーへの委譲) ---
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn current_block(&self) -> Result<BasicBlockId, String> {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.current_block
|
2025-08-18 23:36:40 +09:00
|
|
|
|
.ok_or_else(|| "No current block".to_string())
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn new_block(&mut self) -> BasicBlockId {
|
|
|
|
|
|
self.parent_builder.block_gen.next()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn new_value(&mut self) -> ValueId {
|
2025-11-17 17:02:01 +09:00
|
|
|
|
// Use function-local allocator via MirBuilder helper to keep
|
|
|
|
|
|
// ValueId ranges consistent within the current function.
|
|
|
|
|
|
self.parent_builder.next_value_id()
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn set_current_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
|
|
|
|
|
self.parent_builder.start_new_block(block_id)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.emit_instruction(MirInstruction::Jump { target })
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn emit_branch(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ValueId,
|
|
|
|
|
|
then_bb: BasicBlockId,
|
|
|
|
|
|
else_bb: BasicBlockId,
|
|
|
|
|
|
) -> Result<(), String> {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
// LocalSSA: ensure condition is materialized in the current block
|
|
|
|
|
|
let condition_local = self.parent_builder.local_ssa_ensure(condition, 4);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.emit_instruction(MirInstruction::Branch {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
condition: condition_local,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
then_bb,
|
|
|
|
|
|
else_bb,
|
|
|
|
|
|
})
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn emit_const(&mut self, dst: ValueId, value: ConstValue) -> Result<(), String> {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.emit_instruction(MirInstruction::Const { dst, value })
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
/// ブロック先頭に PHI 命令を挿入(不変条件: PHI は常にブロック先頭)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn emit_phi_at_block_start(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
block_id: BasicBlockId,
|
|
|
|
|
|
dst: ValueId,
|
|
|
|
|
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
|
|
|
|
|
) -> Result<(), String> {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// Phi nodeをブロックの先頭に挿入
|
|
|
|
|
|
if let Some(ref mut function) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(block) = function.get_block_mut(block_id) {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] Block {} current instructions count: {}",
|
|
|
|
|
|
block_id,
|
|
|
|
|
|
block.instructions.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// Phi命令は必ずブロックの先頭に配置。ただし同一dstの既存PHIがある場合は差し替える。
|
|
|
|
|
|
let mut replaced = false;
|
|
|
|
|
|
let mut idx = 0;
|
|
|
|
|
|
while idx < block.instructions.len() {
|
|
|
|
|
|
match &mut block.instructions[idx] {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
MirInstruction::Phi {
|
|
|
|
|
|
dst: d,
|
|
|
|
|
|
inputs: ins,
|
|
|
|
|
|
} if *d == dst => {
|
2025-11-02 10:58:09 +09:00
|
|
|
|
*ins = inputs.clone();
|
|
|
|
|
|
replaced = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
MirInstruction::Phi { .. } => {
|
|
|
|
|
|
idx += 1;
|
|
|
|
|
|
}
|
2025-11-02 10:58:09 +09:00
|
|
|
|
_ => break,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if !replaced {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
let phi_inst = MirInstruction::Phi {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
inputs: inputs.clone(),
|
|
|
|
|
|
};
|
2025-11-02 10:58:09 +09:00
|
|
|
|
block.instructions.insert(0, phi_inst);
|
|
|
|
|
|
}
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0");
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] Block {} after insert instructions count: {}",
|
|
|
|
|
|
block_id,
|
|
|
|
|
|
block.instructions.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
// Verify PHI is still there
|
|
|
|
|
|
if let Some(first_inst) = block.instructions.get(0) {
|
|
|
|
|
|
match first_inst {
|
|
|
|
|
|
MirInstruction::Phi { dst: phi_dst, .. } => {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] Verified: First instruction is PHI dst=%{}",
|
|
|
|
|
|
phi_dst.0
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
}
|
|
|
|
|
|
other => {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}",
|
|
|
|
|
|
other
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
Ok(())
|
|
|
|
|
|
} else {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] ❌ Block {} not found!", block_id);
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
Err(format!("Block {} not found", block_id))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] ❌ No current function!");
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
Err("No current function".to_string())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-18 13:35:38 +09:00
|
|
|
|
#[allow(dead_code)]
|
2025-08-18 23:36:40 +09:00
|
|
|
|
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())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// Variable Map Utilities — snapshots and rebinding
|
|
|
|
|
|
// =============================================================
|
2025-11-22 05:33:40 +09:00
|
|
|
|
fn get_current_variable_map(&self) -> BTreeMap<String, ValueId> { // Phase 25.1: BTreeMap化
|
2025-08-18 23:36:40 +09:00
|
|
|
|
self.parent_builder.variable_map.clone()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn update_variable(&mut self, name: String, value: ValueId) {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] LoopBuilder::update_variable: name={}, value=%{}",
|
|
|
|
|
|
name, value.0
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
self.parent_builder.variable_map.insert(name, value);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-26 01:42:18 +09:00
|
|
|
|
fn get_variable_at_block(&self, name: &str, block_id: BasicBlockId) -> Option<ValueId> {
|
|
|
|
|
|
// まずブロックごとのスナップショットを優先
|
|
|
|
|
|
if let Some(map) = self.block_var_maps.get(&block_id) {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if let Some(v) = map.get(name) {
|
|
|
|
|
|
return Some(*v);
|
|
|
|
|
|
}
|
2025-08-26 01:42:18 +09:00
|
|
|
|
}
|
|
|
|
|
|
// フォールバック:現在の変数マップ(単純ケース用)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
self.parent_builder.variable_map.get(name).copied()
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn build_statement(&mut self, stmt: ASTNode) -> Result<ValueId, String> {
|
2025-09-10 17:39:46 +09:00
|
|
|
|
match stmt {
|
2025-09-21 08:53:00 +09:00
|
|
|
|
// 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)?);
|
2025-09-23 09:00:07 +09:00
|
|
|
|
// フェーズS修正:統一終端検出ユーティリティ使用
|
|
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
2025-09-21 08:53:00 +09:00
|
|
|
|
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
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
ASTNode::If {
|
|
|
|
|
|
condition,
|
|
|
|
|
|
then_body,
|
|
|
|
|
|
else_body,
|
|
|
|
|
|
..
|
|
|
|
|
|
} => self.lower_if_in_loop(*condition, then_body, else_body),
|
2025-09-22 21:52:39 +09:00
|
|
|
|
ASTNode::Break { .. } => self.do_break(),
|
|
|
|
|
|
ASTNode::Continue { .. } => self.do_continue(),
|
|
|
|
|
|
other => self.parent_builder.build_expression(other),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-15 22:14:42 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
/// 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> {
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Reserve a deterministic join id for debug region labeling (nested inside loop)
|
|
|
|
|
|
let join_id = self.parent_builder.debug_next_join_id();
|
2025-09-26 04:17:56 +09:00
|
|
|
|
// Pre-pin comparison operands to slots so repeated uses across blocks are safe
|
2025-09-26 05:28:20 +09:00
|
|
|
|
if crate::config::env::mir_pre_pin_compare_operands() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
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");
|
|
|
|
|
|
}
|
2025-09-26 04:17:56 +09:00
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
_ => {}
|
2025-09-26 04:17:56 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// 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();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
let pre_branch_bb = self.current_block()?;
|
2025-09-22 21:52:39 +09:00
|
|
|
|
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();
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let trace_if = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");
|
2025-09-25 10:23:14 +09:00
|
|
|
|
// (legacy) kept for earlier merge style; now unified helpers compute deltas directly.
|
2025-09-22 21:52:39 +09:00
|
|
|
|
|
|
|
|
|
|
// then branch
|
|
|
|
|
|
self.set_current_block(then_bb)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: join then-branch (inside loop)
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_push_region(format!("join#{}", join_id) + "/then");
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Materialize all variables at entry via single-pred Phi (correctness-first)
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let names_then: Vec<String> = self
|
|
|
|
|
|
.parent_builder
|
|
|
|
|
|
.variable_map
|
|
|
|
|
|
.keys()
|
|
|
|
|
|
.filter(|n| !n.starts_with("__pin$"))
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.collect();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
for name in names_then {
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if let Some(&pre_v) = pre_if_var_map.get(&name) {
|
2025-09-26 05:28:20 +09:00
|
|
|
|
let phi_val = self.new_value();
|
|
|
|
|
|
self.emit_phi_at_block_start(then_bb, phi_val, vec![(pre_branch_bb, pre_v)])?;
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let name_for_log = name.clone();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.update_variable(name, phi_val);
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace_if {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[if-trace] then-entry phi var={} pre={:?} -> dst={:?}",
|
|
|
|
|
|
name_for_log, pre_v, phi_val
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-26 05:28:20 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-22 21:52:39 +09:00
|
|
|
|
for s in then_body.iter().cloned() {
|
|
|
|
|
|
let _ = self.build_statement(s)?;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// フェーズS修正:統一終端検出ユーティリティ使用
|
2025-11-21 06:22:21 +09:00
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
}
|
2025-09-22 21:52:39 +09:00
|
|
|
|
}
|
|
|
|
|
|
let then_var_map_end = self.get_current_variable_map();
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// フェーズS修正:最強モード指摘の「実到達predecessor捕捉」を統一
|
2025-11-21 06:22:21 +09:00
|
|
|
|
let then_pred_to_merge =
|
|
|
|
|
|
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Pop then-branch debug region
|
|
|
|
|
|
self.parent_builder.debug_pop_region();
|
2025-09-22 21:52:39 +09:00
|
|
|
|
|
|
|
|
|
|
// else branch
|
|
|
|
|
|
self.set_current_block(else_bb)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: join else-branch (inside loop)
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_push_region(format!("join#{}", join_id) + "/else");
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Materialize all variables at entry via single-pred Phi (correctness-first)
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let names2: Vec<String> = self
|
|
|
|
|
|
.parent_builder
|
|
|
|
|
|
.variable_map
|
|
|
|
|
|
.keys()
|
|
|
|
|
|
.filter(|n| !n.starts_with("__pin$"))
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.collect();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
for name in names2 {
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if let Some(&pre_v) = pre_if_var_map.get(&name) {
|
2025-09-26 05:28:20 +09:00
|
|
|
|
let phi_val = self.new_value();
|
|
|
|
|
|
self.emit_phi_at_block_start(else_bb, phi_val, vec![(pre_branch_bb, pre_v)])?;
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let name_for_log = name.clone();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
self.update_variable(name, phi_val);
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace_if {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
|
|
|
|
|
|
name_for_log, pre_v, phi_val
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-26 05:28:20 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-22 05:33:40 +09:00
|
|
|
|
let mut else_var_map_end_opt: Option<BTreeMap<String, ValueId>> = None;
|
2025-09-22 21:52:39 +09:00
|
|
|
|
if let Some(es) = else_body.clone() {
|
|
|
|
|
|
for s in es.into_iter() {
|
|
|
|
|
|
let _ = self.build_statement(s)?;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// フェーズS修正:統一終端検出ユーティリティ使用
|
2025-11-21 06:22:21 +09:00
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
}
|
2025-09-22 21:52:39 +09:00
|
|
|
|
}
|
|
|
|
|
|
else_var_map_end_opt = Some(self.get_current_variable_map());
|
|
|
|
|
|
}
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// フェーズS修正:else branchでも統一実到達predecessor捕捉
|
2025-11-21 06:22:21 +09:00
|
|
|
|
let else_pred_to_merge =
|
|
|
|
|
|
capture_actual_predecessor_and_jump(self.parent_builder, merge_bb)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Pop else-branch debug region
|
|
|
|
|
|
self.parent_builder.debug_pop_region();
|
2025-09-15 22:14:42 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// Continue at merge
|
|
|
|
|
|
self.set_current_block(merge_bb)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: join merge (inside loop)
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_push_region(format!("join#{}", join_id) + "/join");
|
2025-09-22 09:32:54 +09:00
|
|
|
|
|
2025-11-22 05:33:40 +09:00
|
|
|
|
// Phase 25.1: HashSet → BTreeSet(決定性確保)
|
|
|
|
|
|
let mut vars: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
|
2025-11-21 06:22:21 +09:00
|
|
|
|
let then_prog = ASTNode::Program {
|
|
|
|
|
|
statements: then_body.clone(),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-25 10:23:14 +09:00
|
|
|
|
crate::mir::phi_core::if_phi::collect_assigned_vars(&then_prog, &mut vars);
|
2025-09-22 21:52:39 +09:00
|
|
|
|
if let Some(es) = &else_body {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
let else_prog = ASTNode::Program {
|
|
|
|
|
|
statements: es.clone(),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-25 10:23:14 +09:00
|
|
|
|
crate::mir::phi_core::if_phi::collect_assigned_vars(&else_prog, &mut vars);
|
2025-09-22 21:52:39 +09:00
|
|
|
|
}
|
2025-09-22 09:32:54 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// Reset to pre-if map before rebinding to ensure a clean environment
|
|
|
|
|
|
self.parent_builder.variable_map = pre_if_var_map.clone();
|
2025-11-22 07:05:21 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 26-E: PhiBuilderBox 統合
|
|
|
|
|
|
// Ops構造体: PhiMergeOps(Legacy)と PhiBuilderOps(新)の両対応
|
2025-09-25 10:23:14 +09:00
|
|
|
|
struct Ops<'b, 'a>(&'b mut LoopBuilder<'a>);
|
2025-11-22 07:05:21 +09:00
|
|
|
|
|
2025-09-25 10:23:14 +09:00
|
|
|
|
impl<'b, 'a> crate::mir::phi_core::if_phi::PhiMergeOps for Ops<'b, 'a> {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
fn new_value(&mut self) -> ValueId {
|
|
|
|
|
|
self.0.new_value()
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
fn emit_phi_at_block_start(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
block: BasicBlockId,
|
|
|
|
|
|
dst: ValueId,
|
|
|
|
|
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
2025-11-21 06:22:21 +09:00
|
|
|
|
) -> 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)],
|
|
|
|
|
|
) {
|
2025-09-25 10:23:14 +09:00
|
|
|
|
if let Some(ref func) = self.0.parent_builder.current_function {
|
|
|
|
|
|
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
|
2025-09-16 06:13:44 +09:00
|
|
|
|
}
|
2025-09-15 22:14:42 +09:00
|
|
|
|
}
|
2025-09-10 17:39:46 +09:00
|
|
|
|
}
|
2025-11-22 07:05:21 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 26-E: PhiBuilderOps trait 実装(箱理論統一)
|
|
|
|
|
|
impl<'b, 'a> crate::mir::phi_core::phi_builder_box::PhiBuilderOps for Ops<'b, 'a> {
|
|
|
|
|
|
fn new_value(&mut self) -> ValueId {
|
|
|
|
|
|
self.0.new_value()
|
|
|
|
|
|
}
|
|
|
|
|
|
fn emit_phi(
|
|
|
|
|
|
&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 get_block_predecessors(&self, block: BasicBlockId) -> Vec<BasicBlockId> {
|
|
|
|
|
|
if let Some(ref func) = self.0.parent_builder.current_function {
|
|
|
|
|
|
func.blocks
|
|
|
|
|
|
.get(&block)
|
|
|
|
|
|
.map(|bb| bb.predecessors.iter().copied().collect())
|
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Vec::new()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
fn emit_void(&mut self) -> ValueId {
|
|
|
|
|
|
let void_id = self.0.new_value();
|
|
|
|
|
|
let _ = self.0.emit_const(void_id, ConstValue::Void);
|
|
|
|
|
|
void_id
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 3-A: Loop PHI生成用メソッド実装
|
|
|
|
|
|
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
|
|
|
|
|
|
self.0.parent_builder.current_block = Some(block);
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn block_exists(&self, block: BasicBlockId) -> bool {
|
|
|
|
|
|
if let Some(ref func) = self.0.parent_builder.current_function {
|
|
|
|
|
|
func.blocks.contains_key(&block)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
// Reset to pre-if snapshot, then delegate to shared helper
|
|
|
|
|
|
self.parent_builder.variable_map = pre_if_var_map.clone();
|
2025-11-18 19:05:46 +09:00
|
|
|
|
|
2025-11-18 19:21:26 +09:00
|
|
|
|
// 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());
|
|
|
|
|
|
|
2025-11-19 02:44:40 +09:00
|
|
|
|
// Region/GC 観測レイヤ(Phase 25.1l):
|
|
|
|
|
|
// NYASH_REGION_TRACE=1 のときだけ、Stage‑B 周辺 If 構造の
|
|
|
|
|
|
// Region 情報(entry/exit/slots)をログに出すよ。
|
|
|
|
|
|
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
|
|
|
|
|
|
|
|
|
|
|
|
let mut ops = Ops(self);
|
|
|
|
|
|
|
2025-11-22 07:05:21 +09:00
|
|
|
|
// Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成)
|
|
|
|
|
|
// Legacy: merge_modified_with_control() → New: PhiBuilderBox::generate_phis()
|
|
|
|
|
|
let mut phi_builder = crate::mir::phi_core::phi_builder_box::PhiBuilderBox::new();
|
|
|
|
|
|
let post_snapshots = if let Some(ref else_map) = else_var_map_end_opt {
|
|
|
|
|
|
vec![then_var_map_end.clone(), else_map.clone()]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
vec![then_var_map_end.clone()]
|
|
|
|
|
|
};
|
|
|
|
|
|
phi_builder.generate_phis(&mut ops, &form, &pre_if_var_map, &post_snapshots)?;
|
2025-11-18 18:56:35 +09:00
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-22 21:52:39 +09:00
|
|
|
|
let void_id = self.new_value();
|
|
|
|
|
|
self.emit_const(void_id, ConstValue::Void)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Pop merge debug region
|
|
|
|
|
|
self.parent_builder.debug_pop_region();
|
2025-09-22 21:52:39 +09:00
|
|
|
|
Ok(void_id)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
2025-08-26 01:42:18 +09:00
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
|
|
|
|
|
|
// Implement phi_core LoopPhiOps on LoopBuilder for in-place delegation
|
|
|
|
|
|
impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
fn new_value(&mut self) -> ValueId {
|
|
|
|
|
|
self.new_value()
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
fn update_var(&mut self, name: String, value: ValueId) {
|
|
|
|
|
|
self.update_variable(name, value)
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
2025-11-21 06:22:21 +09:00
|
|
|
|
fn debug_verify_phi_inputs(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
merge_bb: BasicBlockId,
|
|
|
|
|
|
inputs: &[(BasicBlockId, ValueId)],
|
|
|
|
|
|
) {
|
2025-09-25 10:23:14 +09:00
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
|
|
|
|
|
|
fn emit_copy_at_preheader(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
preheader_id: BasicBlockId,
|
|
|
|
|
|
dst: ValueId,
|
|
|
|
|
|
src: ValueId,
|
|
|
|
|
|
) -> Result<(), String> {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
if let Some(ref mut function) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(block) = function.get_block_mut(preheader_id) {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] Adding Copy instruction to block {}",
|
|
|
|
|
|
preheader_id
|
|
|
|
|
|
);
|
2025-11-02 08:23:43 +09:00
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
block.add_instruction(MirInstruction::Copy { dst, src });
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
} else {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id);
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
Err(format!("Preheader block {} not found", preheader_id))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-11-02 08:23:43 +09:00
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] ❌ No current function!");
|
|
|
|
|
|
}
|
2025-11-01 16:31:48 +09:00
|
|
|
|
Err("No current function".to_string())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-02 10:58:09 +09:00
|
|
|
|
|
|
|
|
|
|
fn add_predecessor_edge(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
block: BasicBlockId,
|
|
|
|
|
|
pred: BasicBlockId,
|
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
|
self.add_predecessor(block, pred)
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
// Implement LoopFormOps trait for LoopBuilder to support LoopFormBuilder integration
|
|
|
|
|
|
impl<'a> LoopFormOps for LoopBuilder<'a> {
|
|
|
|
|
|
fn new_value(&mut self) -> ValueId {
|
2025-11-18 06:39:45 +09:00
|
|
|
|
// 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() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[LoopFormOps::new_value] fn='{}' counter: {} -> {}, allocated: {:?}",
|
|
|
|
|
|
func.signature.name, before, func.next_value_id, id
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
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() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[LoopFormOps::new_value] FALLBACK value_gen, allocated: {:?}",
|
|
|
|
|
|
id
|
|
|
|
|
|
);
|
2025-11-18 06:39:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-21 06:22:21 +09:00
|
|
|
|
fn get_block_predecessors(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
block: BasicBlockId,
|
|
|
|
|
|
) -> std::collections::HashSet<BasicBlockId> {
|
2025-11-18 09:26:39 +09:00
|
|
|
|
// 📦 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 {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
std::collections::HashSet::new() // Non-existent blocks have no predecessors
|
2025-11-18 09:26:39 +09:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::collections::HashSet::new()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 09:49:13 +09:00
|
|
|
|
/// 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);
|
2025-11-17 05:24:07 +09:00
|
|
|
|
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[is_parameter] ValueId({}) -> {} (kind = {:?})",
|
|
|
|
|
|
value_id.0,
|
|
|
|
|
|
is_param,
|
|
|
|
|
|
self.parent_builder.get_value_kind(value_id)
|
|
|
|
|
|
);
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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> {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.emit_instruction(MirInstruction::Copy { dst, src })
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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> {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
self.emit_phi_at_block_start(self.current_block()?, dst, inputs)
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn update_phi_inputs(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
block: BasicBlockId,
|
|
|
|
|
|
phi_id: ValueId,
|
|
|
|
|
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
|
|
|
|
|
) -> Result<(), String> {
|
2025-11-21 06:22:21 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.update_phi_instruction(block, phi_id, inputs)
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|