2025-08-18 23:36:40 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* MIR Loop Builder - SSA形式でのループ構築専用モジュール
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-18 23:36:40 +09:00
|
|
|
|
* Sealed/Unsealed blockとPhi nodeを使った正しいループ実装
|
|
|
|
|
|
* Based on Gemini's recommendation for proper SSA loop handling
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
|
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
2025-09-25 10:23:14 +09:00
|
|
|
|
use crate::mir::phi_core::loop_phi::IncompletePhi;
|
2025-11-17 05:24:07 +09:00
|
|
|
|
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
2025-08-18 23:36:40 +09:00
|
|
|
|
use crate::ast::ASTNode;
|
2025-09-23 07:41:08 +09:00
|
|
|
|
use std::collections::HashMap;
|
2025-08-18 23:36:40 +09:00
|
|
|
|
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// Phase 15 段階的根治戦略:制御フローユーティリティ
|
|
|
|
|
|
use super::utils::{
|
|
|
|
|
|
is_current_block_terminated,
|
|
|
|
|
|
capture_actual_predecessor_and_jump,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-25 10:23:14 +09:00
|
|
|
|
// IncompletePhi has moved to phi_core::loop_phi
|
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
|
|
|
|
/// ループ内で追跡する変数の不完全Phi node
|
|
|
|
|
|
incomplete_phis: HashMap<BasicBlockId, Vec<IncompletePhi>>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ブロックごとの変数マップ(スコープ管理)
|
2025-08-21 12:14:33 +09:00
|
|
|
|
#[allow(dead_code)]
|
2025-08-18 23:36:40 +09:00
|
|
|
|
block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>>,
|
2025-09-10 17:39:46 +09:00
|
|
|
|
|
|
|
|
|
|
/// ループヘッダーID(continueで使用)
|
|
|
|
|
|
loop_header: Option<BasicBlockId>,
|
|
|
|
|
|
|
|
|
|
|
|
/// continue文からの変数スナップショット
|
|
|
|
|
|
continue_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
|
2025-09-23 09:48:29 +09:00
|
|
|
|
/// break文からの変数スナップショット(exit PHI生成用)
|
|
|
|
|
|
exit_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
|
|
|
|
|
|
|
2025-09-23 07:25:58 +09:00
|
|
|
|
// フェーズM: no_phi_modeフィールド削除(常にPHI使用)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 06:13:44 +09:00
|
|
|
|
|
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
|
|
|
|
|
|
fn find_copy_source(&self, block_id: BasicBlockId, dst: ValueId) -> Option<ValueId> {
|
|
|
|
|
|
let func = self.parent_builder.current_function.as_ref()?;
|
|
|
|
|
|
let block = func.blocks.get(&block_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
let trace = std::env::var("NYASH_LOOP_TRACE").ok().as_deref() == Some("1");
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop/copy-trace] searching for dst={:?} in block={:?}, {} instructions",
|
|
|
|
|
|
dst, block_id, block.instructions.len());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (idx, inst) in block.instructions.iter().enumerate() {
|
|
|
|
|
|
if let MirInstruction::Copy { dst: inst_dst, src } = inst {
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop/copy-trace] inst#{}: %{} = copy %{}", idx, inst_dst.0, src.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
if *inst_dst == dst {
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop/copy-trace] FOUND! dst={:?} src={:?}", dst, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
return Some(*src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop/copy-trace] NOT FOUND for dst={:?}", dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Handle a `break` statement: jump to loop exit and continue in a fresh unreachable block.
|
|
|
|
|
|
fn do_break(&mut self) -> Result<ValueId, String> {
|
2025-09-23 09:48:29 +09:00
|
|
|
|
// Snapshot variables at break point for exit PHI generation
|
|
|
|
|
|
let snapshot = self.get_current_variable_map();
|
|
|
|
|
|
let cur_block = self.current_block()?;
|
|
|
|
|
|
self.exit_snapshots.push((cur_block, snapshot));
|
|
|
|
|
|
|
2025-09-19 02:07:38 +09:00
|
|
|
|
if let Some(exit_bb) = crate::mir::builder::loops::current_exit(self.parent_builder) {
|
|
|
|
|
|
self.jump_with_pred(exit_bb)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.switch_to_unreachable_block_with_void()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Handle a `continue` statement: snapshot vars, jump to loop header, then continue in a fresh unreachable block.
|
|
|
|
|
|
fn do_continue(&mut self) -> Result<ValueId, String> {
|
|
|
|
|
|
// Snapshot variables at current block to be considered as a predecessor input
|
|
|
|
|
|
let snapshot = self.get_current_variable_map();
|
|
|
|
|
|
let cur_block = self.current_block()?;
|
|
|
|
|
|
self.block_var_maps.insert(cur_block, snapshot.clone());
|
|
|
|
|
|
self.continue_snapshots.push((cur_block, snapshot));
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(header) = self.loop_header {
|
|
|
|
|
|
self.jump_with_pred(header)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.switch_to_unreachable_block_with_void()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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 {
|
2025-09-23 07:25:58 +09:00
|
|
|
|
// フェーズM: no_phi_mode初期化削除
|
2025-08-18 23:36:40 +09:00
|
|
|
|
Self {
|
|
|
|
|
|
parent_builder: parent,
|
|
|
|
|
|
incomplete_phis: HashMap::new(),
|
|
|
|
|
|
block_var_maps: HashMap::new(),
|
2025-09-10 17:39:46 +09:00
|
|
|
|
loop_header: None,
|
|
|
|
|
|
continue_snapshots: Vec::new(),
|
2025-09-23 09:48:29 +09:00
|
|
|
|
exit_snapshots: Vec::new(), // exit PHI用のスナップショット
|
2025-09-23 07:25:58 +09:00
|
|
|
|
// フェーズM: no_phi_modeフィールド削除
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-17 05:24:07 +09:00
|
|
|
|
/// SSA形式でループを構築 (Feature flag dispatch)
|
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> {
|
|
|
|
|
|
// Check feature flag for LoopForm PHI v2
|
|
|
|
|
|
let use_loopform_v2 = std::env::var("NYASH_LOOPFORM_PHI_V2")
|
|
|
|
|
|
.map(|v| v == "1" || v.to_lowercase() == "true")
|
|
|
|
|
|
.unwrap_or(false);
|
|
|
|
|
|
|
|
|
|
|
|
if use_loopform_v2 {
|
|
|
|
|
|
self.build_loop_with_loopform(condition, body)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.build_loop_legacy(condition, body)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// SSA形式でループを構築 (LoopFormBuilder implementation)
|
|
|
|
|
|
fn build_loop_with_loopform(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
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 ¤t_vars {
|
|
|
|
|
|
eprintln!(" {} -> {:?}", name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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-17 05:48:03 +09:00
|
|
|
|
// Jump from current block to preheader
|
|
|
|
|
|
self.emit_jump(preheader_id)?;
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
eprintln!("[loopform] variable_map at loop entry (size={}):", current_vars.len());
|
|
|
|
|
|
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-17 05:24:07 +09:00
|
|
|
|
let is_param = self.is_parameter(name);
|
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-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
loopform.prepare_structure(self, ¤t_vars)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 2: Emit preheader (copies and jump to header)
|
|
|
|
|
|
loopform.emit_preheader(self)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 3: Emit header PHIs (incomplete, only preheader edge)
|
|
|
|
|
|
self.set_current_block(header_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure header block exists before emitting PHIs
|
|
|
|
|
|
self.parent_builder.ensure_block_exists(header_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
loopform.emit_header_phis(self)?;
|
|
|
|
|
|
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[loopform] variable_map after emit_header_phis:");
|
|
|
|
|
|
for (name, value) in self.get_current_variable_map().iter() {
|
|
|
|
|
|
eprintln!(" {} -> {:?}", name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set up loop context for break/continue
|
|
|
|
|
|
crate::mir::builder::loops::push_loop_context(self.parent_builder, header_id, exit_id);
|
|
|
|
|
|
self.loop_header = Some(header_id);
|
|
|
|
|
|
self.continue_snapshots.clear();
|
|
|
|
|
|
self.exit_snapshots.clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Emit condition check in header
|
|
|
|
|
|
let cond_value = self.parent_builder.build_expression(condition)?;
|
|
|
|
|
|
self.emit_branch(cond_value, body_id, exit_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Lower loop body
|
|
|
|
|
|
self.set_current_block(body_id)?;
|
|
|
|
|
|
for stmt in body {
|
|
|
|
|
|
self.build_statement(stmt)?;
|
|
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Capture variable snapshot at end of body (before jumping to latch)
|
|
|
|
|
|
let body_end_vars = self.get_current_variable_map();
|
|
|
|
|
|
|
|
|
|
|
|
// Jump to latch if not already terminated
|
|
|
|
|
|
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
let cur_body_end = self.current_block()?;
|
|
|
|
|
|
self.emit_jump(latch_id)?;
|
|
|
|
|
|
latch_id
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Body is terminated (break/continue), use current block as latch
|
|
|
|
|
|
self.current_block()?
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Latch: jump back to header
|
|
|
|
|
|
self.set_current_block(latch_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Update variable map with body end values for sealing
|
|
|
|
|
|
for (name, value) in body_end_vars {
|
|
|
|
|
|
self.update_variable(name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 4: Seal PHIs with latch values
|
|
|
|
|
|
loopform.seal_phis(self, actual_latch_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Exit block
|
|
|
|
|
|
self.set_current_block(exit_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Build exit PHIs for break statements
|
|
|
|
|
|
let exit_snaps = self.exit_snapshots.clone();
|
|
|
|
|
|
loopform.build_exit_phis(self, exit_id, &exit_snaps)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pop loop context
|
|
|
|
|
|
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
|
|
|
|
|
|
|
|
|
|
|
// Return void value
|
|
|
|
|
|
let void_dst = self.new_value();
|
|
|
|
|
|
self.emit_const(void_dst, ConstValue::Void)?;
|
|
|
|
|
|
Ok(void_dst)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// SSA形式でループを構築 (Legacy implementation)
|
|
|
|
|
|
fn build_loop_legacy(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
2025-08-18 23:36:40 +09:00
|
|
|
|
) -> Result<ValueId, String> {
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Reserve a deterministic loop id for debug region labeling
|
|
|
|
|
|
let loop_id = self.parent_builder.debug_next_loop_id();
|
2025-09-20 06:24:33 +09:00
|
|
|
|
// Pre-scan body for simple carrier pattern (up to 2 assigned variables, no break/continue)
|
|
|
|
|
|
let mut assigned_vars: Vec<String> = Vec::new();
|
|
|
|
|
|
let mut has_ctrl = false;
|
2025-09-25 10:23:14 +09:00
|
|
|
|
for st in &body { crate::mir::phi_core::loop_phi::collect_carrier_assigns(st, &mut assigned_vars, &mut has_ctrl); }
|
2025-09-20 06:24:33 +09:00
|
|
|
|
if !has_ctrl && !assigned_vars.is_empty() && assigned_vars.len() <= 2 {
|
|
|
|
|
|
// Emit a carrier hint (no-op sink by default; visible with NYASH_MIR_TRACE_HINTS=1)
|
|
|
|
|
|
self.parent_builder.hint_loop_carrier(assigned_vars.clone());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 1. ブロックの準備
|
|
|
|
|
|
let preheader_id = self.current_block()?;
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// Snapshot variable map at preheader before switching to header to avoid
|
|
|
|
|
|
// capturing block-local SSA placeholders created on block switch.
|
|
|
|
|
|
let pre_vars_snapshot = self.get_current_variable_map();
|
2025-09-26 14:34:42 +09:00
|
|
|
|
let trace = std::env::var("NYASH_LOOP_TRACE").ok().as_deref() == Some("1");
|
2025-09-17 07:59:41 +09:00
|
|
|
|
let (header_id, body_id, after_loop_id) =
|
|
|
|
|
|
crate::mir::builder::loops::create_loop_blocks(self.parent_builder);
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop] blocks preheader={:?} header={:?} body={:?} exit={:?}",
|
|
|
|
|
|
preheader_id, header_id, body_id, after_loop_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-10 17:39:46 +09:00
|
|
|
|
self.loop_header = Some(header_id);
|
|
|
|
|
|
self.continue_snapshots.clear();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 2. Preheader -> Header へのジャンプ
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
2025-09-17 07:59:41 +09:00
|
|
|
|
let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 3. Headerブロックの準備(unsealed状態)
|
|
|
|
|
|
self.set_current_block(header_id)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: loop header
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_push_region(format!("loop#{}", loop_id) + "/header");
|
2025-09-20 03:37:20 +09:00
|
|
|
|
// Hint: loop header (no-op sink)
|
|
|
|
|
|
self.parent_builder.hint_loop_header();
|
2025-08-21 12:14:33 +09:00
|
|
|
|
let _ = self.mark_block_unsealed(header_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 4. ループ変数のPhi nodeを準備
|
|
|
|
|
|
// ここでは、ループ内で変更される可能性のある変数を事前に検出するか、
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// または変数アクセス時に遅延生成する(再束縛は条件式構築後に行う)
|
|
|
|
|
|
let incs = self.prepare_loop_variables(header_id, preheader_id, &pre_vars_snapshot)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 5. 条件評価(Phi nodeの結果を使用)
|
2025-09-26 04:17:56 +09:00
|
|
|
|
// Heuristic pre-pin: if condition is a comparison, evaluate its operands and pin them
|
|
|
|
|
|
// so that the loop body/next iterations can safely reuse these values across blocks.
|
2025-09-26 05:28:20 +09:00
|
|
|
|
if crate::config::env::mir_pre_pin_compare_operands() {
|
2025-09-26 04:17:56 +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 05:28:20 +09:00
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
let condition_value = self.build_expression_with_phis(condition)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-17 04:41:49 +09:00
|
|
|
|
// Fix for ValueId(17) bug: Add PHI nodes for any pinned variables created during condition evaluation
|
|
|
|
|
|
// When method calls occur in loop conditions (e.g., i < args.length()), pin_to_slot creates
|
|
|
|
|
|
// pinned receiver variables like __pin$*@recv. These must have PHI nodes in the loop header.
|
|
|
|
|
|
let post_cond_vars = self.get_current_variable_map();
|
2025-11-17 06:31:31 +09:00
|
|
|
|
let mut new_pinned_vars: Vec<(String, ValueId, ValueId)> = Vec::new();
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop] post_cond_vars has {} entries", post_cond_vars.len());
|
|
|
|
|
|
}
|
2025-11-17 04:41:49 +09:00
|
|
|
|
for (name, &value) in post_cond_vars.iter() {
|
|
|
|
|
|
if !name.starts_with("__pin$") { continue; }
|
|
|
|
|
|
// Check if this pinned variable existed before condition compilation
|
|
|
|
|
|
let was_in_incs = incs.iter().any(|inc| inc.var_name == *name);
|
2025-11-17 06:31:31 +09:00
|
|
|
|
let was_in_preheader = pre_vars_snapshot.contains_key(name);
|
|
|
|
|
|
if !was_in_incs && !was_in_preheader {
|
|
|
|
|
|
// This is a NEW pinned variable created during condition evaluation (not inherited from preheader)
|
|
|
|
|
|
// We need to find the source of the copy instruction that created this value
|
|
|
|
|
|
let preheader_value = self.find_copy_source(header_id, value).unwrap_or(value);
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop] NEW pinned var: {} value={:?} preheader={:?}", name, value, preheader_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
new_pinned_vars.push((name.clone(), value, preheader_value));
|
|
|
|
|
|
} else if !was_in_incs && was_in_preheader {
|
|
|
|
|
|
// This pinned variable existed in preheader, so it needs a PHI but we should use the preheader value
|
|
|
|
|
|
let preheader_value = pre_vars_snapshot.get(name).copied().unwrap_or(value);
|
|
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop] INHERITED pinned var: {} value={:?} preheader={:?}", name, value, preheader_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
new_pinned_vars.push((name.clone(), value, preheader_value));
|
2025-11-17 04:41:49 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add PHI nodes for new pinned variables in header block
|
2025-11-17 06:31:31 +09:00
|
|
|
|
for (name, value, preheader_value) in new_pinned_vars {
|
2025-11-17 04:41:49 +09:00
|
|
|
|
let phi_id = self.new_value();
|
|
|
|
|
|
self.emit_phi_at_block_start(header_id, phi_id, vec![(preheader_id, preheader_value)])?;
|
|
|
|
|
|
// Update variable map to use PHI value
|
|
|
|
|
|
self.update_variable(name.clone(), phi_id);
|
|
|
|
|
|
// Add to incomplete PHIs for later sealing with latch edges
|
|
|
|
|
|
if let Some(ref mut header_incs) = self.incomplete_phis.get_mut(&header_id) {
|
|
|
|
|
|
header_incs.push(crate::mir::phi_core::loop_phi::IncompletePhi {
|
|
|
|
|
|
phi_id,
|
|
|
|
|
|
var_name: name,
|
|
|
|
|
|
known_inputs: vec![(preheader_id, preheader_value)],
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 6. 条件分岐
|
2025-09-26 05:28:20 +09:00
|
|
|
|
let pre_branch_bb = self.current_block()?;
|
2025-11-17 04:41:49 +09:00
|
|
|
|
|
|
|
|
|
|
// Fix for ValueId UseBeforeDef bug: Build header snapshot from PHI values
|
|
|
|
|
|
// The exit PHI must reference values that are defined AT THE HEADER BLOCK'S EXIT.
|
|
|
|
|
|
// We can't use the current variable_map directly because it might contain values
|
|
|
|
|
|
// that are only partially defined. Instead, use the PHI values from incomplete_phis
|
|
|
|
|
|
// and any new pinned variables created during condition evaluation.
|
|
|
|
|
|
let mut header_exit_snapshot = std::collections::HashMap::new();
|
|
|
|
|
|
|
|
|
|
|
|
// First, collect all PHI values (these are defined at the header entry)
|
|
|
|
|
|
for inc in &incs {
|
|
|
|
|
|
header_exit_snapshot.insert(inc.var_name.clone(), inc.phi_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Then, add any new pinned variables created during condition evaluation
|
|
|
|
|
|
// (these were added as PHIs in lines 204-219)
|
|
|
|
|
|
let post_cond_vars = self.get_current_variable_map();
|
|
|
|
|
|
for (name, &value) in post_cond_vars.iter() {
|
|
|
|
|
|
if !header_exit_snapshot.contains_key(name) {
|
|
|
|
|
|
header_exit_snapshot.insert(name.clone(), value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
self.emit_branch(condition_value, body_id, after_loop_id)?;
|
2025-09-17 07:59:41 +09:00
|
|
|
|
let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, body_id, header_id);
|
|
|
|
|
|
let _ = crate::mir::builder::loops::add_predecessor(self.parent_builder, after_loop_id, header_id);
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop] header branched to body={:?} and exit={:?}",
|
|
|
|
|
|
body_id, after_loop_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-17 04:41:49 +09:00
|
|
|
|
// Save the header snapshot for exit PHI generation
|
|
|
|
|
|
crate::mir::phi_core::loop_phi::save_block_snapshot(
|
|
|
|
|
|
&mut self.block_var_maps,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
&header_exit_snapshot,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// Rebind loop-carried variables to their PHI IDs now that condition is emitted
|
|
|
|
|
|
for inc in &incs {
|
|
|
|
|
|
self.update_variable(inc.var_name.clone(), inc.phi_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 7. ループボディの構築
|
|
|
|
|
|
self.set_current_block(body_id)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: loop body
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_replace_region(format!("loop#{}", loop_id) + "/body");
|
2025-09-26 05:28:20 +09:00
|
|
|
|
// Materialize pinned slots at entry via single-pred Phi
|
2025-11-17 04:41:49 +09:00
|
|
|
|
// IMPORTANT: Use header_exit_snapshot, not current variable_map, to avoid
|
|
|
|
|
|
// referencing values that are defined after the header's branch instruction.
|
|
|
|
|
|
let names: Vec<String> = header_exit_snapshot.keys().cloned().collect();
|
2025-09-26 05:28:20 +09:00
|
|
|
|
for name in names {
|
|
|
|
|
|
if !name.starts_with("__pin$") { continue; }
|
2025-11-17 04:41:49 +09:00
|
|
|
|
if let Some(&pre_v) = header_exit_snapshot.get(&name) {
|
2025-09-26 05:28:20 +09:00
|
|
|
|
let phi_val = self.new_value();
|
|
|
|
|
|
self.emit_phi_at_block_start(body_id, phi_val, vec![(pre_branch_bb, pre_v)])?;
|
|
|
|
|
|
self.update_variable(name, phi_val);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-21 08:53:00 +09:00
|
|
|
|
// Scope enter for loop body
|
|
|
|
|
|
self.parent_builder.hint_scope_enter(0);
|
2025-08-31 03:03:04 +09:00
|
|
|
|
// Optional safepoint per loop-iteration
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if std::env::var("NYASH_BUILDER_SAFEPOINT_LOOP")
|
|
|
|
|
|
.ok()
|
|
|
|
|
|
.as_deref()
|
|
|
|
|
|
== Some("1")
|
|
|
|
|
|
{
|
2025-08-31 03:03:04 +09:00
|
|
|
|
self.emit_safepoint()?;
|
|
|
|
|
|
}
|
2025-08-26 01:42:18 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// ボディをビルド
|
|
|
|
|
|
for stmt in body {
|
|
|
|
|
|
self.build_statement(stmt)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 8. Latchブロック(ボディの最後)からHeaderへ戻る
|
2025-09-14 19:16:32 +09:00
|
|
|
|
// 現在の挿入先が latch(最後のブロック)なので、そのブロックIDでスナップショットを保存する
|
2025-08-18 23:36:40 +09:00
|
|
|
|
let latch_id = self.current_block()?;
|
2025-09-20 03:37:20 +09:00
|
|
|
|
// Hint: loop latch (no-op sink)
|
|
|
|
|
|
self.parent_builder.hint_loop_latch();
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: loop latch (end of body)
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_replace_region(format!("loop#{}", loop_id) + "/latch");
|
2025-09-21 08:53:00 +09:00
|
|
|
|
// Scope leave for loop body
|
|
|
|
|
|
self.parent_builder.hint_scope_leave(0);
|
2025-09-14 19:16:32 +09:00
|
|
|
|
let latch_snapshot = self.get_current_variable_map();
|
|
|
|
|
|
// 以前は body_id に保存していたが、複数ブロックのボディや continue 混在時に不正確になるため
|
|
|
|
|
|
// 実際の latch_id に対してスナップショットを紐づける
|
2025-09-25 10:23:14 +09:00
|
|
|
|
crate::mir::phi_core::loop_phi::save_block_snapshot(
|
|
|
|
|
|
&mut self.block_var_maps,
|
|
|
|
|
|
latch_id,
|
|
|
|
|
|
&latch_snapshot,
|
|
|
|
|
|
);
|
2025-09-22 07:54:25 +09:00
|
|
|
|
// Only jump back to header if the latch block is not already terminated
|
|
|
|
|
|
{
|
|
|
|
|
|
let need_jump = {
|
|
|
|
|
|
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(bb) = fun_ro.get_block(latch_id) {
|
|
|
|
|
|
!bb.is_terminated()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
if need_jump {
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
|
|
|
|
|
let _ = crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
latch_id,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// 9. Headerブロックをシール(全predecessors確定)
|
|
|
|
|
|
self.seal_block(header_id, latch_id)?;
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop] sealed header={:?} with latch={:?}",
|
|
|
|
|
|
header_id, latch_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-23 09:48:29 +09:00
|
|
|
|
// 10. ループ後の処理 - Exit PHI生成
|
2025-08-18 23:36:40 +09:00
|
|
|
|
self.set_current_block(after_loop_id)?;
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Debug region: loop exit
|
|
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.debug_replace_region(format!("loop#{}", loop_id) + "/exit");
|
2025-09-23 09:48:29 +09:00
|
|
|
|
|
|
|
|
|
|
// Exit PHIの生成 - break時点での変数値を統一
|
|
|
|
|
|
self.create_exit_phis(header_id, after_loop_id)?;
|
|
|
|
|
|
|
2025-09-15 22:14:42 +09:00
|
|
|
|
// Pop loop context
|
2025-09-16 03:54:44 +09:00
|
|
|
|
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
2025-09-28 01:33:58 +09:00
|
|
|
|
// Pop debug region scope
|
|
|
|
|
|
self.parent_builder.debug_pop_region();
|
2025-09-15 22:14:42 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// void値を返す
|
|
|
|
|
|
let void_dst = self.new_value();
|
|
|
|
|
|
self.emit_const(void_dst, ConstValue::Void)?;
|
2025-09-26 14:34:42 +09:00
|
|
|
|
if trace {
|
|
|
|
|
|
eprintln!("[loop] exit={:?} return void=%{:?}", after_loop_id, void_dst);
|
|
|
|
|
|
}
|
2025-08-18 23:36:40 +09:00
|
|
|
|
Ok(void_dst)
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// PHI Helpers — prepare/finalize PHIs and block sealing
|
|
|
|
|
|
// =============================================================
|
2025-08-18 23:36:40 +09:00
|
|
|
|
/// ループ変数の準備(事前検出または遅延生成)
|
|
|
|
|
|
fn prepare_loop_variables(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
header_id: BasicBlockId,
|
|
|
|
|
|
preheader_id: BasicBlockId,
|
2025-11-02 10:58:09 +09:00
|
|
|
|
pre_vars_snapshot: &std::collections::HashMap<String, ValueId>,
|
|
|
|
|
|
) -> Result<Vec<crate::mir::phi_core::loop_phi::IncompletePhi>, String> {
|
2025-11-01 16:31:48 +09:00
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
|
|
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
|
|
let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst);
|
|
|
|
|
|
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// Use the variable map captured at preheader (before switching to header)
|
|
|
|
|
|
let current_vars = pre_vars_snapshot.clone();
|
2025-11-02 08:23:43 +09:00
|
|
|
|
// Debug: print current_vars before prepare (guarded by env)
|
|
|
|
|
|
let dbg = std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1");
|
|
|
|
|
|
if dbg {
|
|
|
|
|
|
eprintln!("[DEBUG] prepare_loop_variables call #{}", count);
|
|
|
|
|
|
eprintln!("[DEBUG] current_vars = {:?}", current_vars);
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG] preheader_id = {:?}, header_id = {:?}",
|
|
|
|
|
|
preheader_id, header_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-25 10:23:14 +09:00
|
|
|
|
crate::mir::phi_core::loop_phi::save_block_snapshot(
|
|
|
|
|
|
&mut self.block_var_maps,
|
|
|
|
|
|
preheader_id,
|
|
|
|
|
|
¤t_vars,
|
|
|
|
|
|
);
|
|
|
|
|
|
let incs = crate::mir::phi_core::loop_phi::prepare_loop_variables_with(
|
|
|
|
|
|
self,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
preheader_id,
|
|
|
|
|
|
¤t_vars,
|
|
|
|
|
|
)?;
|
2025-11-02 10:58:09 +09:00
|
|
|
|
// Defer variable rebinding to PHI IDs until after the loop condition is emitted.
|
|
|
|
|
|
// Store incomplete PHIs for later sealing and for rebinding after branch emission.
|
|
|
|
|
|
self.incomplete_phis.insert(header_id, incs.clone());
|
|
|
|
|
|
Ok(incs)
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
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
|
|
|
|
fn seal_block(&mut self, block_id: BasicBlockId, latch_id: BasicBlockId) -> Result<(), String> {
|
2025-08-18 23:36:40 +09:00
|
|
|
|
if let Some(incomplete_phis) = self.incomplete_phis.remove(&block_id) {
|
2025-09-25 10:23:14 +09:00
|
|
|
|
let cont_snaps = self.continue_snapshots.clone();
|
|
|
|
|
|
crate::mir::phi_core::loop_phi::seal_incomplete_phis_with(
|
|
|
|
|
|
self,
|
|
|
|
|
|
block_id,
|
|
|
|
|
|
latch_id,
|
|
|
|
|
|
incomplete_phis,
|
|
|
|
|
|
&cont_snaps,
|
|
|
|
|
|
)?;
|
2025-08-18 23:36:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
self.mark_block_sealed(block_id)?;
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-23 09:48:29 +09:00
|
|
|
|
/// Exitブロックで変数のPHIを生成(breakポイントでの値を統一)
|
|
|
|
|
|
fn create_exit_phis(&mut self, header_id: BasicBlockId, exit_id: BasicBlockId) -> Result<(), String> {
|
2025-11-17 04:41:49 +09:00
|
|
|
|
// Use the saved header block snapshot instead of current variable map
|
|
|
|
|
|
// The current block at this point is the exit block, not the header,
|
|
|
|
|
|
// so we must retrieve the header's snapshot from block_var_maps.
|
|
|
|
|
|
let header_vars = self.block_var_maps
|
|
|
|
|
|
.get(&header_id)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.unwrap_or_else(|| self.get_current_variable_map());
|
2025-09-25 10:23:14 +09:00
|
|
|
|
let exit_snaps = self.exit_snapshots.clone();
|
|
|
|
|
|
crate::mir::phi_core::loop_phi::build_exit_phis_with(
|
|
|
|
|
|
self,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
exit_id,
|
|
|
|
|
|
&header_vars,
|
|
|
|
|
|
&exit_snaps,
|
|
|
|
|
|
)
|
2025-09-23 09:48:29 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
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 {
|
|
|
|
|
|
self.parent_builder.value_gen.next()
|
|
|
|
|
|
}
|
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_safepoint(&mut self) -> Result<(), String> {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.parent_builder
|
|
|
|
|
|
.emit_instruction(MirInstruction::Safepoint)
|
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] {
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
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-08-21 12:14:33 +09:00
|
|
|
|
fn mark_block_unsealed(&mut self, _block_id: BasicBlockId) -> Result<(), String> {
|
2025-08-18 23:36:40 +09:00
|
|
|
|
// ブロックはデフォルトでunsealedなので、特に何もしない
|
|
|
|
|
|
// (既にBasicBlock::newでsealed: falseに初期化されている)
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn mark_block_sealed(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
|
|
|
|
|
if let Some(ref mut function) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(block) = function.get_block_mut(block_id) {
|
|
|
|
|
|
block.seal();
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err(format!("Block {} not found", block_id))
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err("No current function".to_string())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// Variable Map Utilities — snapshots and rebinding
|
|
|
|
|
|
// =============================================================
|
2025-08-18 23:36:40 +09:00
|
|
|
|
fn get_current_variable_map(&self) -> HashMap<String, ValueId> {
|
|
|
|
|
|
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_expression_with_phis(&mut self, expr: ASTNode) -> Result<ValueId, String> {
|
|
|
|
|
|
// Phi nodeの結果を考慮しながら式を構築
|
|
|
|
|
|
self.parent_builder.build_expression(expr)
|
|
|
|
|
|
}
|
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-09-22 21:52:39 +09:00
|
|
|
|
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),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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-09-26 04:17:56 +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 05:28:20 +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修正:統一終端検出ユーティリティ使用
|
|
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
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捕捉」を統一
|
|
|
|
|
|
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-09-22 21:52:39 +09:00
|
|
|
|
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)?;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// フェーズS修正:統一終端検出ユーティリティ使用
|
|
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
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捕捉
|
|
|
|
|
|
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-09-22 21:52:39 +09:00
|
|
|
|
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() };
|
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 {
|
|
|
|
|
|
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-09-25 10:23:14 +09:00
|
|
|
|
// 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);
|
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-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();
|
|
|
|
|
|
let mut ops = Ops(self);
|
|
|
|
|
|
crate::mir::phi_core::if_phi::merge_modified_at_merge_with(
|
|
|
|
|
|
&mut ops,
|
|
|
|
|
|
merge_bb,
|
|
|
|
|
|
then_bb,
|
|
|
|
|
|
else_bb,
|
|
|
|
|
|
then_pred_to_merge,
|
|
|
|
|
|
else_pred_to_merge,
|
|
|
|
|
|
&pre_if_var_map,
|
|
|
|
|
|
&then_var_map_end,
|
|
|
|
|
|
&else_var_map_end_opt,
|
|
|
|
|
|
None,
|
|
|
|
|
|
)?;
|
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<'_> {
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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 {
|
|
|
|
|
|
eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
|
|
|
|
|
|
}
|
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-17 06:31:31 +09:00
|
|
|
|
// Use function-local allocator via MirBuilder helper to keep
|
|
|
|
|
|
// ValueId ranges consistent with the current function's value_count.
|
|
|
|
|
|
self.parent_builder.next_value_id()
|
2025-11-17 05:24:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_parameter(&self, name: &str) -> bool {
|
|
|
|
|
|
// A parameter is a true function parameter that doesn't change across iterations
|
|
|
|
|
|
// Pinned receivers (__pin$*$@*) are NOT parameters - they're carriers
|
|
|
|
|
|
// because they can be reassigned in the loop body
|
|
|
|
|
|
|
|
|
|
|
|
// Pinned variables are always carriers (loop-variant)
|
|
|
|
|
|
if name.starts_with("__pin$") {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check if it's the receiver
|
|
|
|
|
|
if name == "me" {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check if it's in the original function parameter names
|
|
|
|
|
|
// This is more reliable than checking ValueIds, which can change through copies/PHIs
|
|
|
|
|
|
let is_param = self.parent_builder.function_param_names.contains(name);
|
|
|
|
|
|
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[is_parameter] {} -> {} (param_names = {:?})",
|
|
|
|
|
|
name, is_param, self.parent_builder.function_param_names);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
is_param
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
|
|
|
|
|
|
self.parent_builder.start_new_block(block)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn emit_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), String> {
|
|
|
|
|
|
self.parent_builder.emit_instruction(MirInstruction::Copy { dst, src })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
|
|
|
|
|
|
self.emit_jump(target)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn emit_phi(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
dst: ValueId,
|
|
|
|
|
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
|
self.emit_phi_at_block_start(
|
|
|
|
|
|
self.current_block()?,
|
|
|
|
|
|
dst,
|
|
|
|
|
|
inputs
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn update_phi_inputs(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
block: BasicBlockId,
|
|
|
|
|
|
phi_id: ValueId,
|
|
|
|
|
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
|
self.parent_builder.update_phi_instruction(block, phi_id, inputs)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn update_var(&mut self, name: String, value: ValueId) {
|
|
|
|
|
|
self.parent_builder.variable_map.insert(name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_variable_at_block(&self, name: &str, block: BasicBlockId) -> Option<ValueId> {
|
|
|
|
|
|
// Use the inherent method to avoid recursion
|
|
|
|
|
|
LoopBuilder::get_variable_at_block(self, name, block)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|