2025-11-29 12:44:40 +09:00
|
|
|
|
use super::{ConstValue, LoopBuilder, ValueId};
|
|
|
|
|
|
use crate::ast::ASTNode;
|
|
|
|
|
|
use crate::mir::control_form::{is_control_form_trace_on, ControlForm, LoopShape};
|
|
|
|
|
|
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
|
|
|
|
|
use crate::mir::utils::is_current_block_terminated;
|
|
|
|
|
|
use crate::mir::{BasicBlockId, MirInstruction};
|
|
|
|
|
|
use std::collections::{BTreeMap, BTreeSet};
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> LoopBuilder<'a> {
|
|
|
|
|
|
/// SSA形式でループを構築 (LoopForm v2 only)
|
|
|
|
|
|
pub fn build_loop(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
|
|
|
|
|
// Phase 7-F: Legacy loop builder removed - LoopForm v2 is now the only implementation
|
|
|
|
|
|
self.build_loop_with_loopform(condition, body)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// SSA形式でループを構築 (LoopFormBuilder implementation)
|
|
|
|
|
|
fn build_loop_with_loopform(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
body: Vec<ASTNode>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[build_loop_with_loopform] === ENTRY ===");
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
eprintln!("[build_loop_with_loopform] condition={:?}", condition);
|
|
|
|
|
|
eprintln!("[build_loop_with_loopform] body.len()={}", body.len());
|
|
|
|
|
|
}
|
|
|
|
|
|
// Create loop structure blocks following LLVM canonical form
|
|
|
|
|
|
// We need a dedicated preheader block to materialize loop entry copies
|
|
|
|
|
|
let before_loop_id = self.current_block()?;
|
|
|
|
|
|
|
|
|
|
|
|
// Capture variable snapshot BEFORE creating new blocks (at loop entry point)
|
|
|
|
|
|
let current_vars = self.get_current_variable_map();
|
|
|
|
|
|
|
|
|
|
|
|
// DEBUG: Show variable map before guard check
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] before_loop_id={:?}, variable_map size={}",
|
|
|
|
|
|
before_loop_id,
|
|
|
|
|
|
current_vars.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
for (name, value) in ¤t_vars {
|
|
|
|
|
|
eprintln!(" {} -> {:?}", name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 25.3: GUARD check removed - ValueId(0) is valid for first parameters
|
|
|
|
|
|
// Previous code incorrectly assumed ValueId(0) always meant uninitialized variables,
|
|
|
|
|
|
// but it's actually the correct ID for the first parameter in functions like:
|
|
|
|
|
|
// skip_whitespace(s, idx) -> s=ValueId(0), idx=ValueId(1)
|
|
|
|
|
|
// This caused loops in such functions to be entirely skipped.
|
|
|
|
|
|
|
|
|
|
|
|
let preheader_id = self.new_block();
|
|
|
|
|
|
let header_id = self.new_block();
|
|
|
|
|
|
let body_id = self.new_block();
|
|
|
|
|
|
let latch_id = self.new_block();
|
|
|
|
|
|
let exit_id = self.new_block();
|
|
|
|
|
|
// Phase 25.1q: canonical continue merge block
|
|
|
|
|
|
// All continue 文は一度このブロックに集約してから header へ戻る。
|
|
|
|
|
|
let continue_merge_id = self.new_block();
|
|
|
|
|
|
|
|
|
|
|
|
// Jump from current block to preheader
|
|
|
|
|
|
let entry_block = self.current_block()?;
|
|
|
|
|
|
self.emit_jump(preheader_id)?;
|
|
|
|
|
|
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
preheader_id,
|
|
|
|
|
|
entry_block,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize LoopFormBuilder with preheader and header blocks
|
|
|
|
|
|
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 1: Prepare structure (allocate all ValueIds upfront)
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}",
|
|
|
|
|
|
preheader_id, header_id, body_id, latch_id, exit_id);
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] variable_map at loop entry (size={}):",
|
|
|
|
|
|
current_vars.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
let mut loop_count = 0;
|
|
|
|
|
|
for (name, value) in ¤t_vars {
|
|
|
|
|
|
loop_count += 1;
|
|
|
|
|
|
eprintln!(" [{}] {} -> {:?}", loop_count, name, value);
|
|
|
|
|
|
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
|
|
|
|
|
let is_param = self.is_parameter(*value);
|
|
|
|
|
|
eprintln!(" param={}", is_param);
|
|
|
|
|
|
}
|
|
|
|
|
|
eprintln!("[loopform] iterated {} times", loop_count);
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
eprintln!("[loopform] BEFORE prepare_structure: current_function=None");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
loopform.prepare_structure(self, ¤t_vars)?;
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
eprintln!("[loopform] AFTER prepare_structure: current_function=None");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 2: Emit preheader (copies and jump to header)
|
|
|
|
|
|
loopform.emit_preheader(self)?;
|
|
|
|
|
|
// 📦 Hotfix 6: Add CFG predecessor for header from preheader (same as legacy version)
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 3: Emit header PHIs (incomplete, only preheader edge)
|
|
|
|
|
|
self.set_current_block(header_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Ensure header block exists before emitting PHIs
|
|
|
|
|
|
self.parent_builder.ensure_block_exists(header_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグ統一取得
|
|
|
|
|
|
let fn_name = self
|
|
|
|
|
|
.parent_builder
|
|
|
|
|
|
.current_function
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.clone())
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
|
|
let bypass_flags = crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name);
|
|
|
|
|
|
|
|
|
|
|
|
if bypass_flags.header {
|
|
|
|
|
|
// Phase 27.4-C: JoinIR 実験経路では Header φ を生成しない。
|
|
|
|
|
|
// Pinned/Carrier の値は preheader の copy をそのまま使う。
|
|
|
|
|
|
//
|
|
|
|
|
|
// ⚠️ 重要: このモードでは MIR は不完全(φ 抜け)であり、VM で実行できない。
|
|
|
|
|
|
// JoinIR runner 専用モードであることに注意。
|
|
|
|
|
|
if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() {
|
|
|
|
|
|
eprintln!("[loopform/27.4-C] Header φ bypass active for: {}", fn_name);
|
|
|
|
|
|
eprintln!("[loopform/27.4-C] Skipping emit_header_phis() - using preheader values directly");
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 従来どおり HeaderPhiBuilder を使って φ を準備
|
|
|
|
|
|
loopform.emit_header_phis(self)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() {
|
|
|
|
|
|
eprintln!("[loopform] variable_map after emit_header_phis:");
|
|
|
|
|
|
for (name, value) in self.get_current_variable_map().iter() {
|
|
|
|
|
|
eprintln!(" {} -> {:?}", name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set up loop context for break/continue
|
|
|
|
|
|
crate::mir::builder::loops::push_loop_context(self.parent_builder, header_id, exit_id);
|
|
|
|
|
|
self.loop_header = Some(header_id);
|
|
|
|
|
|
// 既定の continue 先を canonical continue_merge ブロックにする。
|
|
|
|
|
|
// ここを差し替えることで do_loop_exit(Continue) のターゲットを一元化する。
|
|
|
|
|
|
self.continue_target = Some(continue_merge_id);
|
|
|
|
|
|
self.continue_snapshots.clear();
|
|
|
|
|
|
self.exit_snapshots.clear();
|
|
|
|
|
|
|
|
|
|
|
|
// [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
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE build_expression: current_block={:?}",
|
|
|
|
|
|
self.current_block()?
|
|
|
|
|
|
);
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let cond_value = self.parent_builder.build_expression(condition)?;
|
|
|
|
|
|
// Capture the ACTUAL block that emits the branch (might differ from header_id
|
|
|
|
|
|
// if build_expression created new blocks)
|
|
|
|
|
|
let branch_source_block = self.current_block()?;
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] AFTER build_expression: branch_source_block={:?}",
|
|
|
|
|
|
branch_source_block
|
|
|
|
|
|
);
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
|
|
|
|
|
|
func.signature.name, func.next_value_id, func as *const _
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.emit_branch(cond_value, body_id, exit_id)?;
|
|
|
|
|
|
// 📦 Hotfix 6: Add CFG predecessors for branch targets (Cytron et al. 1991 requirement)
|
|
|
|
|
|
// This ensures exit_block.predecessors is populated before Exit PHI generation
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}",
|
|
|
|
|
|
exit_id, branch_source_block
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
body_id,
|
|
|
|
|
|
branch_source_block,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
exit_id,
|
|
|
|
|
|
branch_source_block,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] AFTER emit_branch: current_block={:?}",
|
|
|
|
|
|
self.current_block()?
|
|
|
|
|
|
);
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
|
|
|
|
|
|
body_id, exit_id, branch_source_block
|
|
|
|
|
|
);
|
|
|
|
|
|
// Verify predecessors were added
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(exit_block) = func.blocks.get(&exit_id) {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/condition] exit_block.predecessors = {:?}",
|
|
|
|
|
|
exit_block.predecessors
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Lower loop body
|
|
|
|
|
|
self.set_current_block(body_id)?;
|
|
|
|
|
|
for stmt in body {
|
|
|
|
|
|
self.build_statement(stmt)?;
|
|
|
|
|
|
if is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Capture variable snapshot at end of body (before jumping to latch)
|
|
|
|
|
|
let body_end_vars = self.get_current_variable_map();
|
|
|
|
|
|
|
|
|
|
|
|
// Step 5-1: Writes集合収集(選択肢2+3統合: Snapshot比較で再代入検出)
|
|
|
|
|
|
// current_vars (preheader) と body_end_vars を比較し、ValueId が変わった変数を特定
|
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
let mut writes = HashSet::new();
|
|
|
|
|
|
for (name, &body_value) in &body_end_vars {
|
|
|
|
|
|
// Skip __pin$ temporary variables - they are always BodyLocalInternal
|
|
|
|
|
|
// (Task先生の発見: これらをcarrier扱いすると未定義ValueIdエラーの原因になる)
|
|
|
|
|
|
if name.starts_with("__pin$") && name.contains("$@") {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(&base_value) = current_vars.get(name) {
|
|
|
|
|
|
if body_value != base_value {
|
|
|
|
|
|
writes.insert(name.clone());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// else: body で新規定義された変数(body-local)、header PHI 不要
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DEBUG: Log writes collection
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!("[loopform/writes] === WRITES COLLECTION (Step 5-1) ===");
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/writes] {} variables modified in loop body",
|
|
|
|
|
|
writes.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
let mut sorted_writes: Vec<_> = writes.iter().collect();
|
|
|
|
|
|
sorted_writes.sort();
|
|
|
|
|
|
for name in &sorted_writes {
|
|
|
|
|
|
eprintln!("[loopform/writes] WRITE: {}", name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Jump to latch if not already terminated
|
|
|
|
|
|
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
|
|
|
|
|
self.emit_jump(latch_id)?;
|
|
|
|
|
|
latch_id
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Body is terminated (break/continue), use current block as latch
|
|
|
|
|
|
self.current_block()?
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Latch: jump back to header
|
|
|
|
|
|
self.set_current_block(latch_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Update variable map with body end values for sealing
|
|
|
|
|
|
for (name, value) in &body_end_vars {
|
|
|
|
|
|
self.update_variable(name.clone(), *value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
|
|
|
|
|
// 📦 Hotfix 6: Add CFG predecessor for header from latch (same as legacy version)
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, latch_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 25.1c/k: body-local 変数の PHI 生成
|
|
|
|
|
|
// BreakFinderBox / FuncScannerBox 等で、loop body 内で新規宣言された local 変数が
|
|
|
|
|
|
// loop header に戻った時に undefined になる問題を修正
|
|
|
|
|
|
//
|
|
|
|
|
|
// Step 5-5-B: EXPERIMENTAL - Body-local Header PHI generation DISABLED
|
|
|
|
|
|
// Reason: Option C design states body-local variables should NOT have header PHIs
|
|
|
|
|
|
// - BodyLocalExit: needs EXIT PHI only, NOT header PHI
|
|
|
|
|
|
// - BodyLocalInternal: needs NO PHI at all
|
|
|
|
|
|
//
|
|
|
|
|
|
// TODO Step 5-3: Integrate Option C classification (LoopVarClassBox) here
|
|
|
|
|
|
//
|
|
|
|
|
|
// TEMPORARY DISABLE to test hypothesis that header PHIs are the root cause
|
|
|
|
|
|
let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1");
|
|
|
|
|
|
|
|
|
|
|
|
// DISABLED: Body-local header PHI generation
|
|
|
|
|
|
// This code was causing undefined value errors because it created header PHIs
|
|
|
|
|
|
// for variables that should only have exit PHIs (or no PHIs at all)
|
|
|
|
|
|
if false { // Disabled for Step 5-5-B experiment
|
|
|
|
|
|
// [Original code removed - see git history if needed]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Pass 4: Generate continue_merge PHIs first, then seal header PHIs
|
|
|
|
|
|
// Phase 25.1c/k: canonical continue_merge ブロックで PHI を生成してから seal_phis を呼ぶ
|
|
|
|
|
|
let raw_continue_snaps = self.continue_snapshots.clone();
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: continue_merge ブロックで PHI 生成(merged_snapshot を作る)
|
|
|
|
|
|
// Phase 25.2: LoopSnapshotMergeBox を使って整理
|
|
|
|
|
|
self.set_current_block(continue_merge_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
let merged_snapshot: BTreeMap<String, ValueId> = if !raw_continue_snaps.is_empty() {
|
|
|
|
|
|
if trace_loop_phi {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Generating PHI nodes for {} continue paths",
|
|
|
|
|
|
raw_continue_snaps.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// すべての continue snapshot に現れる変数を収集
|
|
|
|
|
|
let mut all_vars: BTreeMap<String, Vec<(BasicBlockId, ValueId)>> = BTreeMap::new();
|
|
|
|
|
|
for (continue_bb, snapshot) in &raw_continue_snaps {
|
|
|
|
|
|
for (var_name, &value) in snapshot {
|
|
|
|
|
|
all_vars
|
|
|
|
|
|
.entry(var_name.clone())
|
|
|
|
|
|
.or_default()
|
|
|
|
|
|
.push((*continue_bb, value));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 各変数について PHI ノードを生成
|
|
|
|
|
|
// ========================================
|
|
|
|
|
|
// Phase 59b: PhiInputCollector インライン化
|
|
|
|
|
|
// ========================================
|
|
|
|
|
|
let mut merged = BTreeMap::new();
|
|
|
|
|
|
for (var_name, inputs) in all_vars {
|
|
|
|
|
|
// Step 1: sanitize (BTreeMap で重複削除&ソート)
|
|
|
|
|
|
let mut sanitized: BTreeMap<BasicBlockId, ValueId> = BTreeMap::new();
|
|
|
|
|
|
for (bb, val) in &inputs {
|
|
|
|
|
|
sanitized.insert(*bb, *val);
|
|
|
|
|
|
}
|
|
|
|
|
|
let final_inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect();
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: optimize_same_value
|
|
|
|
|
|
let same_value = if final_inputs.is_empty() {
|
|
|
|
|
|
None
|
|
|
|
|
|
} else if final_inputs.len() == 1 {
|
|
|
|
|
|
Some(final_inputs[0].1)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let first_val = final_inputs[0].1;
|
|
|
|
|
|
if final_inputs.iter().all(|(_, val)| *val == first_val) {
|
|
|
|
|
|
Some(first_val)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: PHI 生成 or 同一値を使用
|
|
|
|
|
|
let result_value = if let Some(same_val) = same_value {
|
|
|
|
|
|
// 全て同じ値 or 単一入力 → PHI 不要
|
|
|
|
|
|
same_val
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 異なる値を持つ場合は PHI ノードを生成
|
|
|
|
|
|
let phi_id = self.new_value();
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(ref mut func) = self.parent_builder.current_function {
|
|
|
|
|
|
if let Some(merge_block) = func.blocks.get_mut(&continue_merge_id) {
|
|
|
|
|
|
merge_block.add_instruction(MirInstruction::Phi {
|
|
|
|
|
|
dst: phi_id,
|
|
|
|
|
|
inputs: final_inputs,
|
2025-11-30 09:38:28 +09:00
|
|
|
|
type_hint: None, // Phase 63-6
|
2025-11-29 12:44:40 +09:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if trace_loop_phi {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Generated PHI for '{}': {:?}",
|
|
|
|
|
|
var_name, phi_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
phi_id
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
merged.insert(var_name, result_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Note: 変数マップへの反映は seal_phis に委譲(干渉を避ける)
|
|
|
|
|
|
|
|
|
|
|
|
if trace_loop_phi {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loop-phi/continue-merge] Merged {} variables from {} paths",
|
|
|
|
|
|
merged.len(),
|
|
|
|
|
|
raw_continue_snaps.len()
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
merged
|
|
|
|
|
|
} else {
|
|
|
|
|
|
BTreeMap::new()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
self.emit_jump(header_id)?;
|
|
|
|
|
|
crate::mir::builder::loops::add_predecessor(
|
|
|
|
|
|
self.parent_builder,
|
|
|
|
|
|
header_id,
|
|
|
|
|
|
continue_merge_id,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: merged_snapshot を使って seal_phis を呼ぶ
|
|
|
|
|
|
// Phase 25.3: Continue merge PHI実装(Task先生の発見!)
|
|
|
|
|
|
// - continueが無いループでも、Latchブロックの値をHeader PHIに伝播する必要がある
|
|
|
|
|
|
// - これにより、Exit PHIがHeader PHI経由で正しい値を受け取れる
|
|
|
|
|
|
let continue_snaps: Vec<(BasicBlockId, BTreeMap<String, ValueId>)> = {
|
|
|
|
|
|
// まず、merged_snapshot(continue merge PHI結果)を追加
|
|
|
|
|
|
let mut snaps = if !merged_snapshot.is_empty() {
|
|
|
|
|
|
vec![(continue_merge_id, merged_snapshot.clone())]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
vec![]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 27.4C Refactor: Header φ バイパスフラグを統一取得(seal_phis に渡す)
|
|
|
|
|
|
// Note: fn_name は既に line 299-304 で取得済み、String として保持されている
|
|
|
|
|
|
let bypass_flags_for_seal =
|
|
|
|
|
|
crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name);
|
|
|
|
|
|
|
|
|
|
|
|
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
|
|
|
|
|
// Phase 27.4C: header_bypass フラグも渡す
|
|
|
|
|
|
loopform.seal_phis(
|
|
|
|
|
|
self,
|
|
|
|
|
|
actual_latch_id,
|
|
|
|
|
|
&continue_snaps,
|
|
|
|
|
|
&writes,
|
|
|
|
|
|
bypass_flags_for_seal.header,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: seal body-local PHIs (complete the inputs)
|
|
|
|
|
|
// Step 5-5-A: REMOVED - PHIs now created complete with both inputs upfront
|
|
|
|
|
|
// Old sealing code was overwriting our preheader+latch inputs with latch-only,
|
|
|
|
|
|
// causing "phi pred mismatch" errors.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Body-local PHIs are now created at line 408-456 with BOTH inputs:
|
|
|
|
|
|
// - preheader: poison value (variable doesn't exist yet)
|
|
|
|
|
|
// - latch: actual value from loop body
|
|
|
|
|
|
//
|
|
|
|
|
|
// No further sealing is needed!
|
|
|
|
|
|
|
|
|
|
|
|
// Exit block
|
|
|
|
|
|
self.set_current_block(exit_id)?;
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 25.1h: ControlForm統合版に切り替え
|
|
|
|
|
|
// continue / break のターゲットブロックをユニーク化して収集
|
|
|
|
|
|
// Phase 25.1: HashSet → BTreeSet(決定性確保)
|
|
|
|
|
|
let mut break_set: BTreeSet<BasicBlockId> = BTreeSet::new();
|
|
|
|
|
|
for (bb, _) in &self.exit_snapshots {
|
|
|
|
|
|
break_set.insert(*bb);
|
|
|
|
|
|
}
|
|
|
|
|
|
// LoopShape の continue_targets は「header への canonical backedge」を表す。
|
|
|
|
|
|
// continue が一つ以上存在する場合は continue_merge_id を 1 つだけ登録する。
|
|
|
|
|
|
let continue_targets: Vec<BasicBlockId> = if self.continue_snapshots.is_empty() {
|
|
|
|
|
|
Vec::new()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
vec![continue_merge_id]
|
|
|
|
|
|
};
|
|
|
|
|
|
let break_targets: Vec<BasicBlockId> = break_set.into_iter().collect();
|
|
|
|
|
|
|
|
|
|
|
|
let loop_shape = LoopShape {
|
|
|
|
|
|
preheader: preheader_id,
|
|
|
|
|
|
header: header_id,
|
|
|
|
|
|
body: body_id,
|
|
|
|
|
|
latch: latch_id,
|
|
|
|
|
|
exit: exit_id,
|
|
|
|
|
|
continue_targets,
|
|
|
|
|
|
break_targets,
|
|
|
|
|
|
};
|
|
|
|
|
|
let form = ControlForm::from_loop(loop_shape.clone());
|
|
|
|
|
|
|
|
|
|
|
|
// Region/GC 観測レイヤ(Phase 25.1l):
|
|
|
|
|
|
// NYASH_REGION_TRACE=1 のときだけ、Stage‑B 周辺ループの
|
|
|
|
|
|
// Region 情報(entry/exit/slots)をログに出すよ。
|
|
|
|
|
|
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
|
|
|
|
|
|
|
|
|
|
|
|
// Phase 27.6-2: JoinIR Exit φ バイパスチェック
|
|
|
|
|
|
let fn_name = self
|
|
|
|
|
|
.parent_builder
|
|
|
|
|
|
.current_function
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.as_str())
|
|
|
|
|
|
.unwrap_or("");
|
|
|
|
|
|
|
|
|
|
|
|
let exit_bypass = crate::mir::phi_core::loopform_builder::joinir_exit_bypass_enabled()
|
|
|
|
|
|
&& crate::mir::phi_core::loopform_builder::is_joinir_exit_bypass_target(fn_name);
|
|
|
|
|
|
|
|
|
|
|
|
if exit_bypass {
|
|
|
|
|
|
// Phase 27.6-2: JoinIR 実験経路では Exit φ を生成しない。
|
|
|
|
|
|
// ループ内で定義された値だけで exit 後を構成する(JoinIR の k_exit 引数として表現)。
|
|
|
|
|
|
//
|
|
|
|
|
|
// ⚠️ 重要: このモードでは MIR は不完全(Exit φ 抜け)であり、VM で実行できない。
|
|
|
|
|
|
// JoinIR runner 専用モードであることに注意。
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[loopform/exit-bypass] func={} exit={:?} header={:?} (JoinIR experiment only)",
|
|
|
|
|
|
fn_name, exit_id, header_id
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// [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)
|
|
|
|
|
|
let exit_snaps = self.exit_snapshots.clone();
|
|
|
|
|
|
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
|
|
|
|
|
|
&loopform,
|
|
|
|
|
|
self,
|
|
|
|
|
|
&form,
|
|
|
|
|
|
&exit_snaps,
|
|
|
|
|
|
branch_source_block,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Pop loop context
|
|
|
|
|
|
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
|
|
|
|
|
|
|
|
|
|
|
// ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき LoopShape をダンプ
|
|
|
|
|
|
if is_control_form_trace_on() {
|
|
|
|
|
|
form.debug_dump();
|
|
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
|
|
if let Some(ref func) = self.parent_builder.current_function {
|
|
|
|
|
|
loop_shape.debug_validate(func);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Return void value
|
|
|
|
|
|
let void_dst = self.new_value();
|
|
|
|
|
|
self.emit_const(void_dst, ConstValue::Void)?;
|
|
|
|
|
|
Ok(void_dst)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|