refactor(phi_core): Phase 193 - Complete loopform modularization with 4-pass architecture
Phase 193: loopform_builder.rs modularization (1,166 → 102 lines, 91% reduction) ## Module Structure Created ``` src/mir/phi_core/loopform/ ├── mod.rs (102 lines) - Public API coordinator ├── context.rs (148 lines) - ValueId management ├── variable_models.rs (101 lines) - Variable types ├── utils.rs (112 lines) - Utilities ├── exit_phi.rs (96 lines) - Exit PHI builder ├── builder_core.rs (411 lines) - Core builder logic ├── passes/ │ ├── mod.rs (67 lines) - Pass coordinator │ ├── pass1_discovery.rs (156 lines) - Variable discovery │ ├── pass2_preheader.rs (70 lines) - Preheader copies │ ├── pass3_header_phi.rs (106 lines) - Header PHI construction │ └── pass4_seal.rs (276 lines) - PHI completion ``` ## 4-Pass Architecture Explicit ### Pass 1: Variable Discovery (pass1_discovery.rs) - Classify variables as carriers or pinned - Allocate all ValueIds upfront - GUARD protection for invalid ValueIds ### Pass 2: Preheader Copy (pass2_preheader.rs) - Emit deterministic copy instructions - Order: pinned first, carriers second ### Pass 3: Header PHI Construction (pass3_header_phi.rs) - Generate incomplete PHI nodes - First input: preheader_copy (known) - Second input: latch value (unknown) ### Pass 4: PHI Sealing (pass4_seal.rs) - Complete PHI nodes with latch values - Separate pinned/carrier handling - PHI optimization (same-value detection) ## Size Comparison Before: - loopform_builder.rs: 1,166 lines (monolithic) - loopform_passes.rs: 133 lines (documentation stub) - Total: 1,299 lines in 2 files After: - 11 focused modules: 1,645 lines total - Main file (mod.rs): 102 lines (91% reduction) - Largest module: builder_core (411 lines) - Average module size: 150 lines - 4 pass modules: 608 lines (explicit structure) ## Success Criteria Met ✅ Directory structure created with 11 focused modules ✅ 4-pass architecture explicit and clear ✅ cargo build --release succeeds ✅ Loop programs execute correctly ✅ Zero breaking changes (all APIs compatible) ✅ Module documentation comprehensive ✅ All visibility correct (pub/pub(crate) appropriate) ## Files Modified - NEW: src/mir/phi_core/loopform/mod.rs (public API) - NEW: src/mir/phi_core/loopform/builder_core.rs (core builder) - NEW: src/mir/phi_core/loopform/passes/*.rs (4 pass modules) - MOVED: loopform_*.rs → loopform/*.rs (5 files) - DELETED: loopform_builder.rs, loopform_passes.rs - UPDATED: phi_core/mod.rs (import structure) - UPDATED: json_v0_bridge/lowering/loop_.rs (import path) ## Impact - **Maintainability**: Each pass has clear responsibilities - **Testability**: Individual passes can be tested independently - **Documentation**: Comprehensive module and pass documentation - **Modularity**: Clean separation of concerns - **Readability**: No file exceeds 411 lines Phase 1-3 modularization complete. Ready for new feature development.
This commit is contained in:
411
src/mir/phi_core/loopform/builder_core.rs
Normal file
411
src/mir/phi_core/loopform/builder_core.rs
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
/*!
|
||||||
|
* loopform::builder_core – Core LoopFormBuilder orchestration
|
||||||
|
*
|
||||||
|
* This module contains the main LoopFormBuilder struct and coordinates
|
||||||
|
* the 4-pass PHI generation pipeline.
|
||||||
|
*
|
||||||
|
* The builder manages:
|
||||||
|
* - Carrier and pinned variable collections
|
||||||
|
* - Preheader and header block IDs
|
||||||
|
* - Coordination between the 4 passes
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
||||||
|
use crate::mir::{BasicBlockId, ValueId};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use super::variable_models::{CarrierVariable, PinnedVariable};
|
||||||
|
|
||||||
|
/// LoopForm Meta-Box: Structured representation of loop SSA construction
|
||||||
|
///
|
||||||
|
/// Separates loop-visible variables into classes(25.1e/25.2 スコープモデル):
|
||||||
|
/// - Carriers: Modified in loop body, need header/exit PHI nodes.
|
||||||
|
/// - Pinned: Loop-invariant parameters/receivers, need PHI so every header/exit
|
||||||
|
/// edge has a well-defined value but the logical value never changes.
|
||||||
|
/// - Invariants: Not tracked here; they keep the preheader ValueId and never
|
||||||
|
/// participate in PHI construction.
|
||||||
|
/// - Body-local live-out (BodyLocalInOut): Not stored as dedicated structs, but
|
||||||
|
/// detected at exit-phi time in `build_exit_phis` and merged via
|
||||||
|
/// `LoopSnapshotMergeBox::merge_exit`.
|
||||||
|
///
|
||||||
|
/// Key idea: All ValueIds for Carriers/Pinned are allocated upfront before any
|
||||||
|
/// MIR emission, eliminating circular dependency issues in SSA.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoopFormBuilder {
|
||||||
|
pub carriers: Vec<CarrierVariable>,
|
||||||
|
pub pinned: Vec<PinnedVariable>,
|
||||||
|
pub preheader_id: BasicBlockId,
|
||||||
|
pub header_id: BasicBlockId,
|
||||||
|
/// Step 5-2: Preheader snapshot for ValueId comparison (選択肢3)
|
||||||
|
/// Used in seal_phis() to detect which variables are truly modified vs. invariant
|
||||||
|
pub preheader_vars: BTreeMap<String, ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoopFormBuilder {
|
||||||
|
/// Create a new LoopForm builder with specified block IDs
|
||||||
|
pub fn new(preheader_id: BasicBlockId, header_id: BasicBlockId) -> Self {
|
||||||
|
Self {
|
||||||
|
carriers: Vec::new(),
|
||||||
|
pinned: Vec::new(),
|
||||||
|
preheader_id,
|
||||||
|
header_id,
|
||||||
|
preheader_vars: BTreeMap::new(), // Will be set in prepare_structure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pass 1: Allocate all ValueIds for loop structure
|
||||||
|
///
|
||||||
|
/// This is the critical innovation: we allocate ALL ValueIds
|
||||||
|
/// (preheader copies and header PHIs) BEFORE emitting any instructions.
|
||||||
|
/// This guarantees definition-before-use in SSA form.
|
||||||
|
pub fn prepare_structure<O: LoopFormOps>(
|
||||||
|
&mut self,
|
||||||
|
ops: &mut O,
|
||||||
|
current_vars: &BTreeMap<String, ValueId>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
super::passes::pass1_discovery::prepare_structure(self, ops, current_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pass 2: Emit preheader block instructions
|
||||||
|
///
|
||||||
|
/// Emits copy instructions for ALL variables in deterministic order:
|
||||||
|
/// 1. Pinned variables first
|
||||||
|
/// 2. Carrier variables second
|
||||||
|
///
|
||||||
|
/// This ordering ensures consistent ValueId allocation across runs.
|
||||||
|
pub fn emit_preheader<O: LoopFormOps>(&self, ops: &mut O) -> Result<(), String> {
|
||||||
|
super::passes::pass2_preheader::emit_preheader(self, ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pass 3: Emit header block PHI nodes (incomplete)
|
||||||
|
///
|
||||||
|
/// Creates incomplete PHI nodes with only preheader input.
|
||||||
|
/// These will be completed in seal_phis() after loop body is lowered.
|
||||||
|
pub fn emit_header_phis<O: LoopFormOps>(&mut self, ops: &mut O) -> Result<(), String> {
|
||||||
|
super::passes::pass3_header_phi::emit_header_phis(self, ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pass 4: Complete PHI nodes with latch values
|
||||||
|
///
|
||||||
|
/// Seals both pinned and carrier PHI nodes by finding latch values
|
||||||
|
/// and updating PHI inputs.
|
||||||
|
pub fn seal_phis<O: LoopFormOps>(
|
||||||
|
&mut self,
|
||||||
|
ops: &mut O,
|
||||||
|
latch_id: BasicBlockId,
|
||||||
|
continue_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
|
_writes: &std::collections::HashSet<String>, // Step 5-1/5-2: Reserved for future optimization
|
||||||
|
header_bypass: bool, // Phase 27.4C: Header φ バイパスフラグ
|
||||||
|
) -> Result<(), String> {
|
||||||
|
super::passes::pass4_seal::seal_phis(
|
||||||
|
self,
|
||||||
|
ops,
|
||||||
|
latch_id,
|
||||||
|
continue_snapshots,
|
||||||
|
_writes,
|
||||||
|
header_bypass,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build exit PHIs for break/continue merge points
|
||||||
|
///
|
||||||
|
/// Similar to header PHIs, but merges:
|
||||||
|
/// - Header fallthrough (normal loop exit)
|
||||||
|
/// - Break snapshots (early exit from loop body)
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `branch_source_block`: The ACTUAL block that emitted the branch to exit
|
||||||
|
/// (might differ from header_id if condition evaluation created new blocks)
|
||||||
|
/// Option C: Build exit PHIs with variable classification
|
||||||
|
///
|
||||||
|
/// ## 引数
|
||||||
|
///
|
||||||
|
/// - `inspector`: LocalScopeInspectorBox(変数定義位置追跡)
|
||||||
|
/// - 各ブロックでどの変数が定義されているか追跡
|
||||||
|
/// - BodyLocalInternal 変数(全exit predsで定義されていない)を検出
|
||||||
|
/// - これらの変数は exit PHI を生成しない(PHI pred mismatch防止)
|
||||||
|
pub fn build_exit_phis<O: LoopFormOps>(
|
||||||
|
&self,
|
||||||
|
ops: &mut O,
|
||||||
|
exit_id: BasicBlockId,
|
||||||
|
branch_source_block: BasicBlockId,
|
||||||
|
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
|
) -> Result<(), String> {
|
||||||
|
ops.set_current_block(exit_id)?;
|
||||||
|
|
||||||
|
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||||
|
if debug {
|
||||||
|
eprintln!("[DEBUG/exit_phi] ====== Exit PHI Generation ======");
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] exit_id = {:?}, header_id = {:?}, branch_source = {:?}",
|
||||||
|
exit_id, self.header_id, branch_source_block
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] exit_snapshots.len() = {}",
|
||||||
|
exit_snapshots.len()
|
||||||
|
);
|
||||||
|
for (i, (bb, snap)) in exit_snapshots.iter().enumerate() {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] snapshot[{}]: block = {:?}, num_vars = {}",
|
||||||
|
i, bb,
|
||||||
|
snap.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] pinned.len() = {}, carriers.len() = {}",
|
||||||
|
self.pinned.len(),
|
||||||
|
self.carriers.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 25.2: LoopSnapshotMergeBox を使って exit PHI 統合
|
||||||
|
|
||||||
|
// 1. header_vals を準備(pinned + carriers)
|
||||||
|
let mut header_vals = BTreeMap::new();
|
||||||
|
for pinned in &self.pinned {
|
||||||
|
header_vals.insert(pinned.name.clone(), pinned.header_phi);
|
||||||
|
}
|
||||||
|
for carrier in &self.carriers {
|
||||||
|
header_vals.insert(carrier.name.clone(), carrier.header_phi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. body_local_vars を収集(決定的順序のためBTreeSet使用)
|
||||||
|
let mut body_local_names = Vec::new();
|
||||||
|
let mut body_local_set: std::collections::BTreeSet<String> =
|
||||||
|
std::collections::BTreeSet::new();
|
||||||
|
for (_block_id, snapshot) in exit_snapshots {
|
||||||
|
// 決定的順序のため、keysをソートしてからイテレート
|
||||||
|
let mut sorted_keys: Vec<_> = snapshot.keys().collect();
|
||||||
|
sorted_keys.sort();
|
||||||
|
for var_name in sorted_keys {
|
||||||
|
// Step 5-5-D: Skip __pin$ temporary variables in exit PHI generation
|
||||||
|
// These are BodyLocalInternal and should NOT get exit PHIs
|
||||||
|
if var_name.starts_with("__pin$") && var_name.contains("$@") {
|
||||||
|
if debug {
|
||||||
|
eprintln!("[DEBUG/exit_phi] SKIP __pin$ variable: {}", var_name);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_pinned = self.pinned.iter().any(|p| &p.name == var_name);
|
||||||
|
let is_carrier = self.carriers.iter().any(|c| &c.name == var_name);
|
||||||
|
if !is_pinned && !is_carrier && !body_local_set.contains(var_name) {
|
||||||
|
body_local_names.push(var_name.clone());
|
||||||
|
body_local_set.insert(var_name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug && !body_local_names.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] Found {} body-local variables",
|
||||||
|
body_local_names.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option C: Body-local variables should NOT be added to header_vals!
|
||||||
|
// They are defined inside the loop body, not in the header.
|
||||||
|
// The inspector will correctly track which exit preds have them.
|
||||||
|
|
||||||
|
// 📦 Hotfix 6: Filter exit_snapshots to only include valid CFG predecessors
|
||||||
|
let exit_preds = ops.get_block_predecessors(exit_id);
|
||||||
|
if debug {
|
||||||
|
eprintln!("[DEBUG/exit_phi] Exit block predecessors: {:?}", exit_preds);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut filtered_snapshots = Vec::new();
|
||||||
|
for (block_id, snapshot) in exit_snapshots {
|
||||||
|
if !ops.block_exists(*block_id) {
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] ⚠️ Skipping non-existent block {:?}",
|
||||||
|
block_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !exit_preds.contains(block_id) {
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] ⚠️ Skipping block {:?} (not in CFG predecessors)",
|
||||||
|
block_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filtered_snapshots.push((*block_id, snapshot.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Option C: Record snapshots in inspector for availability checking
|
||||||
|
// Phase 69-2: LocalScopeInspectorBox 削除完了
|
||||||
|
// variable_definitions は LoopScopeShape に移行済み(Phase 48-4)
|
||||||
|
// inspector.record_*() 呼び出しは不要(LoopScopeShape 内部で処理)
|
||||||
|
|
||||||
|
// 4. Option C: merge_exit_with_classification() でPHI pred mismatch防止
|
||||||
|
// pinned/carrier名リストを準備
|
||||||
|
let pinned_names: Vec<String> = self.pinned.iter().map(|p| p.name.clone()).collect();
|
||||||
|
let carrier_names: Vec<String> = self.carriers.iter().map(|c| c.name.clone()).collect();
|
||||||
|
|
||||||
|
// exit_preds を Vec に変換
|
||||||
|
let exit_preds_vec: Vec<BasicBlockId> = exit_preds.iter().copied().collect();
|
||||||
|
|
||||||
|
// Phase 69-2: inspector 引数を削除(LoopScopeShape に移行済み)
|
||||||
|
let all_vars = LoopSnapshotMergeBox::merge_exit_with_classification(
|
||||||
|
branch_source_block,
|
||||||
|
&header_vals,
|
||||||
|
&filtered_snapshots,
|
||||||
|
&exit_preds_vec, // ← 実際のCFG predecessorsを渡す
|
||||||
|
&pinned_names,
|
||||||
|
&carrier_names,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Phase 59: PHI生成(PhiInputCollectorインライン化)
|
||||||
|
// ========================================
|
||||||
|
// 旧: PhiInputCollector::new() + add_snapshot + sanitize + optimize_same_value
|
||||||
|
// 新: BTreeMapで直接処理(同等のロジックをインライン展開)
|
||||||
|
for (var_name, inputs) in all_vars {
|
||||||
|
// Step 1: sanitize - BTreeMapで重複削除&ソート
|
||||||
|
let mut sanitized: std::collections::BTreeMap<BasicBlockId, ValueId> =
|
||||||
|
std::collections::BTreeMap::new();
|
||||||
|
for (bb, val) in &inputs {
|
||||||
|
sanitized.insert(*bb, *val);
|
||||||
|
}
|
||||||
|
let final_inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect();
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] Variable '{}': {} inputs",
|
||||||
|
var_name,
|
||||||
|
final_inputs.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: optimize_same_value - 全て同じ値ならPHI不要
|
||||||
|
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 直接バインド
|
||||||
|
if let Some(same_val) = same_value {
|
||||||
|
// 全て同じ値 or 単一入力 → PHI 不要
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] Variable '{}': single/same value, direct binding to {:?}",
|
||||||
|
var_name, same_val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ops.update_var(var_name, same_val);
|
||||||
|
} else {
|
||||||
|
// 異なる値を持つ場合は PHI ノードを生成
|
||||||
|
let phi_id = ops.new_value();
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG/exit_phi] Creating PHI {:?} for var '{}' with {} inputs",
|
||||||
|
phi_id, var_name,
|
||||||
|
final_inputs.len()
|
||||||
|
);
|
||||||
|
for (bb, val) in &final_inputs {
|
||||||
|
eprintln!("[DEBUG/exit_phi] PHI input: pred={:?} val={:?}", bb, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ops.emit_phi(phi_id, final_inputs)?;
|
||||||
|
ops.update_var(var_name, phi_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operations required by LoopFormBuilder
|
||||||
|
///
|
||||||
|
/// This trait abstracts the underlying MIR builder operations,
|
||||||
|
/// allowing LoopFormBuilder to work with both Rust MIR builder
|
||||||
|
/// and selfhost compiler's JSON-based approach.
|
||||||
|
/// Phase 26-E-3: LoopFormOps と PhiBuilderOps の関係(委譲設計)
|
||||||
|
///
|
||||||
|
/// **Design Rationale (ChatGPT + Claude consensus, 2025-11-22):**
|
||||||
|
/// - PhiBuilderOps = 低レベル「PHI命令発行」道具箱
|
||||||
|
/// - LoopFormOps = 高レベル「ループ構造構築」作業場
|
||||||
|
/// - 関係: **has-a(委譲)** ではなく is-a(継承)
|
||||||
|
///
|
||||||
|
/// **実装方針:**
|
||||||
|
/// - LoopFormOps trait: そのまま(継承なし)
|
||||||
|
/// - PhiBuilderOps 実装: 必要な型に個別実装
|
||||||
|
/// - `impl<'a> PhiBuilderOps for LoopBuilder<'a>` で委譲
|
||||||
|
/// - HashSet → Vec 変換 + ソートで決定性保証
|
||||||
|
///
|
||||||
|
/// **段階的統合:**
|
||||||
|
/// 1. If側: PhiBuilderBox 経由(既に完了)
|
||||||
|
/// 2. Loop側: LoopFormOps + ExitPhiBuilder/HeaderPhiBuilder(現状維持)
|
||||||
|
/// 3. 将来: 必要に応じて LoopBuilder に PhiBuilderOps 実装追加
|
||||||
|
pub trait LoopFormOps {
|
||||||
|
/// Allocate a new ValueId
|
||||||
|
fn new_value(&mut self) -> ValueId;
|
||||||
|
|
||||||
|
/// Ensure MirFunction counter is after the given ValueId to prevent collisions
|
||||||
|
/// CRITICAL: Must be called before allocating new ValueIds in LoopForm
|
||||||
|
fn ensure_counter_after(&mut self, max_id: u32) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// 📦 Check if a block exists in the CFG (Hotfix 2: Exit PHI predecessor validation)
|
||||||
|
/// Used to skip non-existent blocks when building exit PHIs.
|
||||||
|
fn block_exists(&self, block: BasicBlockId) -> bool;
|
||||||
|
|
||||||
|
/// 📦 Get actual CFG predecessors for a block (Hotfix 6: PHI input validation)
|
||||||
|
/// Returns the set of blocks that actually branch to this block in the CFG.
|
||||||
|
/// Used to validate exit PHI inputs against actual control flow.
|
||||||
|
/// Phase 69-3: Changed to BTreeSet for determinism
|
||||||
|
fn get_block_predecessors(
|
||||||
|
&self,
|
||||||
|
block: BasicBlockId,
|
||||||
|
) -> std::collections::BTreeSet<BasicBlockId>;
|
||||||
|
|
||||||
|
/// Phase 26-A-4: ValueIdベースのパラメータ判定(型安全化)
|
||||||
|
///
|
||||||
|
/// 旧実装(名前ベース)から新実装(ValueIdベース)に変更。
|
||||||
|
/// MirValueKindによる型安全判定で、GUARDバグを根絶。
|
||||||
|
fn is_parameter(&self, value_id: ValueId) -> bool;
|
||||||
|
|
||||||
|
/// Set current block for instruction emission
|
||||||
|
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Emit a copy instruction: dst = src
|
||||||
|
fn emit_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Emit a jump instruction to target block
|
||||||
|
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Emit a PHI node with given inputs
|
||||||
|
fn emit_phi(
|
||||||
|
&mut self,
|
||||||
|
dst: ValueId,
|
||||||
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Update PHI node inputs (for sealing incomplete PHIs)
|
||||||
|
fn update_phi_inputs(
|
||||||
|
&mut self,
|
||||||
|
block: BasicBlockId,
|
||||||
|
phi_id: ValueId,
|
||||||
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
/// Update variable binding in current scope
|
||||||
|
fn update_var(&mut self, name: String, value: ValueId);
|
||||||
|
|
||||||
|
/// Get variable value at specific block
|
||||||
|
fn get_variable_at_block(&self, name: &str, block: BasicBlockId) -> Option<ValueId>;
|
||||||
|
|
||||||
|
/// Access underlying MirFunction (for liveness/MirQuery)
|
||||||
|
fn mir_function(&self) -> &crate::mir::MirFunction;
|
||||||
|
}
|
||||||
@ -30,8 +30,8 @@ impl ExitPhiBuilder {
|
|||||||
/// * `exit_id` - Exit block ID
|
/// * `exit_id` - Exit block ID
|
||||||
/// * `branch_source_block` - Branch source block
|
/// * `branch_source_block` - Branch source block
|
||||||
/// * `exit_snapshots` - Snapshots from each exit predecessor
|
/// * `exit_snapshots` - Snapshots from each exit predecessor
|
||||||
pub fn build_exit_phis<O: super::loopform_builder::LoopFormOps>(
|
pub fn build_exit_phis<O: super::builder_core::LoopFormOps>(
|
||||||
loopform: &super::loopform_builder::LoopFormBuilder,
|
loopform: &super::builder_core::LoopFormBuilder,
|
||||||
ops: &mut O,
|
ops: &mut O,
|
||||||
exit_id: BasicBlockId,
|
exit_id: BasicBlockId,
|
||||||
branch_source_block: BasicBlockId,
|
branch_source_block: BasicBlockId,
|
||||||
@ -52,8 +52,8 @@ impl ExitPhiBuilder {
|
|||||||
/// * `form` - ControlForm containing loop structure
|
/// * `form` - ControlForm containing loop structure
|
||||||
/// * `exit_snapshots` - Snapshots from exit predecessors
|
/// * `exit_snapshots` - Snapshots from exit predecessors
|
||||||
/// * `branch_source_block` - Branch source block
|
/// * `branch_source_block` - Branch source block
|
||||||
pub fn build_exit_phis_for_control<O: super::loopform_builder::LoopFormOps>(
|
pub fn build_exit_phis_for_control<O: super::builder_core::LoopFormOps>(
|
||||||
loopform: &super::loopform_builder::LoopFormBuilder,
|
loopform: &super::builder_core::LoopFormBuilder,
|
||||||
ops: &mut O,
|
ops: &mut O,
|
||||||
form: &ControlForm,
|
form: &ControlForm,
|
||||||
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
102
src/mir/phi_core/loopform/mod.rs
Normal file
102
src/mir/phi_core/loopform/mod.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
//! LoopForm PHI Construction System
|
||||||
|
//!
|
||||||
|
//! This module implements the LoopForm Meta-Box approach to PHI construction,
|
||||||
|
//! solving the ValueId circular dependency problem by treating loop structure
|
||||||
|
//! as a "Meta-Box" with explicit separation of carriers vs. pinned variables.
|
||||||
|
//!
|
||||||
|
//! # Architecture: 4-Pass PHI Generation Pipeline
|
||||||
|
//!
|
||||||
|
//! The LoopForm system constructs SSA PHI nodes in 4 distinct passes:
|
||||||
|
//!
|
||||||
|
//! ## Pass 1: prepare_structure() - Variable Discovery & ValueId Allocation
|
||||||
|
//! - Classify variables as carriers (modified in loop) or pinned (loop-invariant)
|
||||||
|
//! - Allocate ValueIds upfront for preheader_copy and header_phi
|
||||||
|
//! - Ensure counter is after all existing ValueIds
|
||||||
|
//! - **Key Innovation**: All ValueIds allocated BEFORE any MIR emission
|
||||||
|
//!
|
||||||
|
//! ## Pass 2: emit_preheader() - Preheader Copy Generation
|
||||||
|
//! - Generate copy instructions in preheader block
|
||||||
|
//! - Order: pinned variables first, then carrier variables (deterministic)
|
||||||
|
//! - Jump to header block
|
||||||
|
//!
|
||||||
|
//! ## Pass 3: emit_header_phis() - Header PHI Construction
|
||||||
|
//! - Generate incomplete PHI nodes in header block
|
||||||
|
//! - First input: preheader_copy (known)
|
||||||
|
//! - Second input: latch value (unknown at this point)
|
||||||
|
//!
|
||||||
|
//! ## Pass 4: seal_phis() - PHI Completion
|
||||||
|
//! - Complete PHI nodes by finding latch values
|
||||||
|
//! - Separate handling for pinned (seal_pinned_phis) and carrier (seal_carrier_phis)
|
||||||
|
//! - Update PHI inputs with actual latch values
|
||||||
|
//!
|
||||||
|
//! # Module Structure
|
||||||
|
//!
|
||||||
|
//! - `context`: ValueId management and counter operations
|
||||||
|
//! - `variable_models`: CarrierVariable, PinnedVariable, LoopBypassFlags types
|
||||||
|
//! - `utils`: Debug and bypass utilities
|
||||||
|
//! - `exit_phi`: Exit PHI builder for loop exits
|
||||||
|
//! - `builder_core`: Core LoopFormBuilder implementation
|
||||||
|
//! - `passes`: 4-pass architecture implementation
|
||||||
|
//! - `pass1_discovery`: Variable discovery and classification
|
||||||
|
//! - `pass2_header_phi`: Header PHI construction
|
||||||
|
//! - `pass3_latch`: Latch processing
|
||||||
|
//! - `pass4_exit_phi`: Exit PHI construction
|
||||||
|
//!
|
||||||
|
//! # Example Usage
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! let mut builder = LoopFormBuilder::new(preheader, header);
|
||||||
|
//! builder.prepare_structure(&mut ops, ¤t_vars)?;
|
||||||
|
//! builder.emit_preheader(&mut ops)?;
|
||||||
|
//! builder.emit_header_phis(&mut ops)?;
|
||||||
|
//! // ... loop body construction ...
|
||||||
|
//! builder.seal_phis(&mut ops, &latch_vars)?;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Status
|
||||||
|
//!
|
||||||
|
//! Phase: 25.1b prototype → 25.1m stabilization
|
||||||
|
//! Status: Always-on (NYASH_LOOPFORM_PHI_V2 for compatibility only)
|
||||||
|
//! Phase 191: Modularized into separate submodules
|
||||||
|
//! Phase 193: Complete 4-pass architecture explicit structure
|
||||||
|
|
||||||
|
// Core modules
|
||||||
|
pub mod context;
|
||||||
|
pub mod variable_models;
|
||||||
|
pub mod utils;
|
||||||
|
pub mod exit_phi;
|
||||||
|
pub mod builder_core;
|
||||||
|
pub mod passes;
|
||||||
|
|
||||||
|
// Re-export main types for backward compatibility
|
||||||
|
pub use builder_core::{LoopFormBuilder, LoopFormOps};
|
||||||
|
pub use context::LoopFormContext;
|
||||||
|
pub use variable_models::{CarrierVariable, PinnedVariable, LoopBypassFlags};
|
||||||
|
|
||||||
|
// Re-export utility functions
|
||||||
|
pub use utils::{
|
||||||
|
is_loopform_debug_enabled,
|
||||||
|
get_loop_bypass_flags,
|
||||||
|
joinir_exit_bypass_enabled,
|
||||||
|
is_joinir_exit_bypass_target,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Re-export exit PHI function for backward compatibility
|
||||||
|
pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
||||||
|
loopform: &LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
form: &crate::mir::control_form::ControlForm,
|
||||||
|
exit_snapshots: &[(
|
||||||
|
crate::mir::BasicBlockId,
|
||||||
|
std::collections::BTreeMap<String, crate::mir::ValueId>,
|
||||||
|
)],
|
||||||
|
branch_source_block: crate::mir::BasicBlockId,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
exit_phi::ExitPhiBuilder::build_exit_phis_for_control(
|
||||||
|
loopform,
|
||||||
|
ops,
|
||||||
|
form,
|
||||||
|
exit_snapshots,
|
||||||
|
branch_source_block,
|
||||||
|
)
|
||||||
|
}
|
||||||
67
src/mir/phi_core/loopform/passes/mod.rs
Normal file
67
src/mir/phi_core/loopform/passes/mod.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//! LoopForm 4-Pass PHI Generation Pipeline
|
||||||
|
//!
|
||||||
|
//! This module implements the 4-pass architecture for PHI node construction
|
||||||
|
//! in loops. Each pass has a specific responsibility in the SSA construction
|
||||||
|
//! process.
|
||||||
|
//!
|
||||||
|
//! # The 4 Passes
|
||||||
|
//!
|
||||||
|
//! ## Pass 1: Variable Discovery & ValueId Allocation (`pass1_discovery`)
|
||||||
|
//! - **Purpose**: Identify all variables in the loop and allocate ValueIds upfront
|
||||||
|
//! - **Responsibility**: Classify variables as carriers (modified) or pinned (invariant)
|
||||||
|
//! - **Key Innovation**: All ValueIds allocated BEFORE any MIR emission
|
||||||
|
//! - **Function**: `prepare_structure()`
|
||||||
|
//!
|
||||||
|
//! ## Pass 2: Preheader Copy Generation (`pass2_preheader`)
|
||||||
|
//! - **Purpose**: Generate copy instructions in preheader block
|
||||||
|
//! - **Responsibility**: Emit deterministic copy instructions
|
||||||
|
//! - **Order**: Pinned variables first, then carrier variables
|
||||||
|
//! - **Function**: `emit_preheader()`
|
||||||
|
//!
|
||||||
|
//! ## Pass 3: Header PHI Construction (`pass3_header_phi`)
|
||||||
|
//! - **Purpose**: Generate incomplete PHI nodes in header block
|
||||||
|
//! - **Responsibility**: Create PHI nodes with preheader input only
|
||||||
|
//! - **Note**: Latch input will be added in Pass 4
|
||||||
|
//! - **Function**: `emit_header_phis()`
|
||||||
|
//!
|
||||||
|
//! ## Pass 4: PHI Sealing (`pass4_seal`)
|
||||||
|
//! - **Purpose**: Complete PHI nodes by finding latch values
|
||||||
|
//! - **Responsibility**: Update PHI inputs with actual latch values
|
||||||
|
//! - **Handles**: Both pinned and carrier variables
|
||||||
|
//! - **Function**: `seal_phis()`
|
||||||
|
//!
|
||||||
|
//! # Usage Example
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! // Pass 1: Variable discovery
|
||||||
|
//! builder.prepare_structure(&mut ops, ¤t_vars)?;
|
||||||
|
//!
|
||||||
|
//! // Pass 2: Preheader copies
|
||||||
|
//! builder.emit_preheader(&mut ops)?;
|
||||||
|
//!
|
||||||
|
//! // Pass 3: Header PHIs (incomplete)
|
||||||
|
//! builder.emit_header_phis(&mut ops)?;
|
||||||
|
//!
|
||||||
|
//! // ... loop body construction ...
|
||||||
|
//!
|
||||||
|
//! // Pass 4: Seal PHIs with latch values
|
||||||
|
//! builder.seal_phis(&mut ops, latch_id, &continue_snapshots, &writes, false)?;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Architecture Benefits
|
||||||
|
//!
|
||||||
|
//! - **Explicit separation**: Each pass has clear responsibilities
|
||||||
|
//! - **Testability**: Each pass can be tested independently
|
||||||
|
//! - **Maintainability**: Easy to understand and modify
|
||||||
|
//! - **Determinism**: Predictable ValueId allocation and PHI construction
|
||||||
|
|
||||||
|
pub mod pass1_discovery;
|
||||||
|
pub mod pass2_preheader;
|
||||||
|
pub mod pass3_header_phi;
|
||||||
|
pub mod pass4_seal;
|
||||||
|
|
||||||
|
// Re-export main functions for convenience
|
||||||
|
pub use pass1_discovery::prepare_structure;
|
||||||
|
pub use pass2_preheader::emit_preheader;
|
||||||
|
pub use pass3_header_phi::emit_header_phis;
|
||||||
|
pub use pass4_seal::seal_phis;
|
||||||
156
src/mir/phi_core/loopform/passes/pass1_discovery.rs
Normal file
156
src/mir/phi_core/loopform/passes/pass1_discovery.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
//! Pass 1: Variable Discovery & ValueId Allocation
|
||||||
|
//!
|
||||||
|
//! This is the critical innovation of the LoopForm system: we allocate ALL
|
||||||
|
//! ValueIds (preheader copies and header PHIs) BEFORE emitting any instructions.
|
||||||
|
//! This guarantees definition-before-use in SSA form and eliminates circular
|
||||||
|
//! dependency issues.
|
||||||
|
//!
|
||||||
|
//! # Responsibilities
|
||||||
|
//!
|
||||||
|
//! 1. **Variable Classification**: Separate variables into:
|
||||||
|
//! - **Carriers**: Modified in loop body, need header/exit PHI nodes
|
||||||
|
//! - **Pinned**: Loop-invariant parameters, need PHI for SSA form
|
||||||
|
//!
|
||||||
|
//! 2. **ValueId Pre-allocation**: Allocate all ValueIds upfront:
|
||||||
|
//! - `preheader_copy`: Copy instruction in preheader
|
||||||
|
//! - `header_phi`: PHI node in loop header
|
||||||
|
//!
|
||||||
|
//! 3. **Counter Management**: Ensure MirFunction counter is ahead of all
|
||||||
|
//! existing ValueIds to prevent collisions
|
||||||
|
//!
|
||||||
|
//! 4. **GUARD Protection**: Skip loop construction if invalid ValueIds detected
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! // Input variables: {x: ValueId(1), y: ValueId(2)}
|
||||||
|
//! builder.prepare_structure(&mut ops, ¤t_vars)?;
|
||||||
|
//!
|
||||||
|
//! // After pass 1:
|
||||||
|
//! // - carriers: [CarrierVariable { name: "x", init: 1, copy: 3, phi: 4 }]
|
||||||
|
//! // - pinned: [PinnedVariable { name: "y", param: 2, copy: 5, phi: 6 }]
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::{ValueId};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::mir::phi_core::loopform::builder_core::{LoopFormBuilder, LoopFormOps};
|
||||||
|
use crate::mir::phi_core::loopform::variable_models::{CarrierVariable, PinnedVariable};
|
||||||
|
|
||||||
|
/// Pass 1: Allocate all ValueIds for loop structure
|
||||||
|
///
|
||||||
|
/// This is the critical innovation: we allocate ALL ValueIds
|
||||||
|
/// (preheader copies and header PHIs) BEFORE emitting any instructions.
|
||||||
|
/// This guarantees definition-before-use in SSA form.
|
||||||
|
pub fn prepare_structure<O: LoopFormOps>(
|
||||||
|
builder: &mut LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
current_vars: &BTreeMap<String, ValueId>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let debug_enabled = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||||
|
|
||||||
|
// Step 5-2: Save preheader snapshot for ValueId comparison in seal_phis()
|
||||||
|
builder.preheader_vars = current_vars.clone();
|
||||||
|
|
||||||
|
if debug_enabled {
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!(
|
||||||
|
"[loopform/prepare] === START prepare_structure() === {} variables",
|
||||||
|
current_vars.len()
|
||||||
|
));
|
||||||
|
crate::runtime::get_global_ring0().log.debug("[loopform/prepare] Full variable list:");
|
||||||
|
let mut sorted_vars: Vec<_> = current_vars.iter().collect();
|
||||||
|
sorted_vars.sort_by_key(|(name, _)| name.as_str());
|
||||||
|
for (name, value) in &sorted_vars {
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/prepare] - {} = {:?}", name, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GUARD: Detect invalid ValueId in variable map
|
||||||
|
// ValueId::INVALID (u32::MAX) indicates uninitialized variables
|
||||||
|
// Skip this loop construction attempt if detected (likely a premature build)
|
||||||
|
for (name, &value) in current_vars.iter() {
|
||||||
|
if value == ValueId::INVALID {
|
||||||
|
if debug_enabled {
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/prepare] ⚠️ GUARD: Skipping loop preparation due to invalid ValueId for variable '{}'", name));
|
||||||
|
crate::runtime::get_global_ring0().log.debug("[loopform/prepare] This indicates the loop is being built prematurely before variables are defined");
|
||||||
|
crate::runtime::get_global_ring0().log.debug("[loopform/prepare] Returning Ok(()) to allow retry with properly initialized variables");
|
||||||
|
}
|
||||||
|
// Return Ok to skip this attempt without failing the entire compilation
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRITICAL FIX: Ensure MirFunction counter is ahead of all existing ValueIds
|
||||||
|
// Without this, new_value() can return ValueIds that are already in use
|
||||||
|
let max_existing_id = current_vars.values().map(|v| v.0).max().unwrap_or(0);
|
||||||
|
|
||||||
|
if debug_enabled {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/prepare] Calling ensure_counter_after(max_existing_id={})",
|
||||||
|
max_existing_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.ensure_counter_after(max_existing_id)?;
|
||||||
|
|
||||||
|
// Count variables by classification for summary
|
||||||
|
let mut param_count = 0;
|
||||||
|
let mut carrier_count = 0;
|
||||||
|
|
||||||
|
// Separate variables into carriers and pinned based on parameter status
|
||||||
|
for (name, &value) in current_vars.iter() {
|
||||||
|
// Step 5-3: Skip __pin$ temporary variables (BodyLocalInternal)
|
||||||
|
// These are compiler-generated temporaries that should not have PHI nodes
|
||||||
|
if name.starts_with("__pin$") && name.contains("$@") {
|
||||||
|
if debug_enabled {
|
||||||
|
eprintln!("[loopform/prepare] SKIP __pin$ variable: {}", name);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
||||||
|
if ops.is_parameter(value) {
|
||||||
|
param_count += 1;
|
||||||
|
// Pinned variable (parameter, not modified in loop)
|
||||||
|
let pinned = PinnedVariable {
|
||||||
|
name: name.clone(),
|
||||||
|
param_value: value,
|
||||||
|
preheader_copy: ops.new_value(), // Allocate NOW
|
||||||
|
header_phi: ops.new_value(), // Allocate NOW
|
||||||
|
};
|
||||||
|
if debug_enabled {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||||
|
name, value, pinned.preheader_copy, pinned.header_phi
|
||||||
|
);
|
||||||
|
}
|
||||||
|
builder.pinned.push(pinned);
|
||||||
|
} else {
|
||||||
|
carrier_count += 1;
|
||||||
|
// Carrier variable (local, modified in loop)
|
||||||
|
let carrier = CarrierVariable {
|
||||||
|
name: name.clone(),
|
||||||
|
init_value: value,
|
||||||
|
preheader_copy: ops.new_value(), // Allocate NOW
|
||||||
|
header_phi: ops.new_value(), // Allocate NOW
|
||||||
|
latch_value: ValueId(0), // Will be set during seal (placeholder)
|
||||||
|
};
|
||||||
|
if debug_enabled {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||||
|
name, value, carrier.preheader_copy, carrier.header_phi
|
||||||
|
);
|
||||||
|
}
|
||||||
|
builder.carriers.push(carrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug_enabled {
|
||||||
|
crate::runtime::get_global_ring0().log.debug("[loopform/prepare] === SUMMARY ===");
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/prepare] Total vars: {}", current_vars.len()));
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/prepare] Pinned (params): {}", param_count));
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/prepare] Carriers (locals): {}", carrier_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
70
src/mir/phi_core/loopform/passes/pass2_preheader.rs
Normal file
70
src/mir/phi_core/loopform/passes/pass2_preheader.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//! Pass 2: Preheader Copy Generation
|
||||||
|
//!
|
||||||
|
//! This pass emits copy instructions in the preheader block for all variables
|
||||||
|
//! that participate in the loop. The copies serve as initial values for the
|
||||||
|
//! loop header PHI nodes.
|
||||||
|
//!
|
||||||
|
//! # Responsibilities
|
||||||
|
//!
|
||||||
|
//! 1. **Deterministic Ordering**: Emit copies in consistent order:
|
||||||
|
//! - Pinned variables first
|
||||||
|
//! - Carrier variables second
|
||||||
|
//!
|
||||||
|
//! 2. **Copy Emission**: For each variable:
|
||||||
|
//! - Emit: `preheader_copy = init_value`
|
||||||
|
//! - This creates a stable definition before the loop header
|
||||||
|
//!
|
||||||
|
//! 3. **Jump Emission**: Emit jump to loop header block
|
||||||
|
//!
|
||||||
|
//! # Why Preheader Copies?
|
||||||
|
//!
|
||||||
|
//! The preheader copies serve multiple purposes:
|
||||||
|
//! - **SSA Definition**: Ensure every loop variable has a single definition point
|
||||||
|
//! - **PHI Input**: Provide first input to header PHI nodes
|
||||||
|
//! - **Determinism**: Consistent ValueId allocation across compilations
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! // After Pass 1:
|
||||||
|
//! // - carriers: [{ name: "x", init: 1, copy: 3, phi: 4 }]
|
||||||
|
//! // - pinned: [{ name: "y", param: 2, copy: 5, phi: 6 }]
|
||||||
|
//!
|
||||||
|
//! builder.emit_preheader(&mut ops)?;
|
||||||
|
//!
|
||||||
|
//! // Generated MIR in preheader block:
|
||||||
|
//! // r5 = r2 // pinned first
|
||||||
|
//! // r3 = r1 // carriers second
|
||||||
|
//! // jump @header
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::phi_core::loopform::builder_core::{LoopFormBuilder, LoopFormOps};
|
||||||
|
|
||||||
|
/// Pass 2: Emit preheader block instructions
|
||||||
|
///
|
||||||
|
/// Emits copy instructions for ALL variables in deterministic order:
|
||||||
|
/// 1. Pinned variables first
|
||||||
|
/// 2. Carrier variables second
|
||||||
|
///
|
||||||
|
/// This ordering ensures consistent ValueId allocation across runs.
|
||||||
|
pub fn emit_preheader<O: LoopFormOps>(
|
||||||
|
builder: &LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
ops.set_current_block(builder.preheader_id)?;
|
||||||
|
|
||||||
|
// Emit copies for pinned variables
|
||||||
|
for pinned in &builder.pinned {
|
||||||
|
ops.emit_copy(pinned.preheader_copy, pinned.param_value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit copies for carrier variables
|
||||||
|
for carrier in &builder.carriers {
|
||||||
|
ops.emit_copy(carrier.preheader_copy, carrier.init_value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump to header
|
||||||
|
ops.emit_jump(builder.header_id)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
106
src/mir/phi_core/loopform/passes/pass3_header_phi.rs
Normal file
106
src/mir/phi_core/loopform/passes/pass3_header_phi.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
//! Pass 3: Header PHI Construction
|
||||||
|
//!
|
||||||
|
//! This pass generates incomplete PHI nodes in the loop header block.
|
||||||
|
//! The PHI nodes initially have only the preheader input; the latch input
|
||||||
|
//! will be added in Pass 4 after the loop body is lowered.
|
||||||
|
//!
|
||||||
|
//! # Responsibilities
|
||||||
|
//!
|
||||||
|
//! 1. **Incomplete PHI Generation**: For each variable:
|
||||||
|
//! - Emit: `header_phi = phi [preheader_copy, preheader]`
|
||||||
|
//! - Update variable binding to header_phi
|
||||||
|
//!
|
||||||
|
//! 2. **Receiver Variable Aliasing** (Hotfix 7):
|
||||||
|
//! - Handle `me` → `__pin$N$@recv` aliasing
|
||||||
|
//! - Update all pin levels to point to same PHI
|
||||||
|
//! - Prevents stale ValueId references in nested loops
|
||||||
|
//!
|
||||||
|
//! 3. **Deterministic Order**: Process in consistent order:
|
||||||
|
//! - Pinned variables first
|
||||||
|
//! - Carrier variables second
|
||||||
|
//!
|
||||||
|
//! # Why Incomplete PHIs?
|
||||||
|
//!
|
||||||
|
//! We cannot complete the PHI nodes at this point because:
|
||||||
|
//! - The loop body hasn't been lowered yet
|
||||||
|
//! - We don't know the latch values
|
||||||
|
//! - Continue statements may create additional predecessors
|
||||||
|
//!
|
||||||
|
//! Pass 4 will complete these PHIs after loop body construction.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! // After Pass 2 (preheader):
|
||||||
|
//! // r3 = r1 (carrier preheader copy)
|
||||||
|
//! // r5 = r2 (pinned preheader copy)
|
||||||
|
//! // jump @header
|
||||||
|
//!
|
||||||
|
//! builder.emit_header_phis(&mut ops)?;
|
||||||
|
//!
|
||||||
|
//! // Generated MIR in header block:
|
||||||
|
//! // r6 = phi [r5, @preheader] // pinned (incomplete)
|
||||||
|
//! // r4 = phi [r3, @preheader] // carrier (incomplete)
|
||||||
|
//! // var[y] = r6
|
||||||
|
//! // var[x] = r4
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::phi_core::loopform::builder_core::{LoopFormBuilder, LoopFormOps};
|
||||||
|
|
||||||
|
/// Pass 3: Emit header block PHI nodes (incomplete)
|
||||||
|
///
|
||||||
|
/// Creates incomplete PHI nodes with only preheader input.
|
||||||
|
/// These will be completed in seal_phis() after loop body is lowered.
|
||||||
|
pub fn emit_header_phis<O: LoopFormOps>(
|
||||||
|
builder: &mut LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
ops.set_current_block(builder.header_id)?;
|
||||||
|
|
||||||
|
// Emit PHIs for pinned variables
|
||||||
|
for pinned in &builder.pinned {
|
||||||
|
ops.emit_phi(
|
||||||
|
pinned.header_phi,
|
||||||
|
vec![(builder.preheader_id, pinned.preheader_copy)],
|
||||||
|
)?;
|
||||||
|
ops.update_var(pinned.name.clone(), pinned.header_phi);
|
||||||
|
|
||||||
|
// 🔧 Hotfix 7 (Enhanced): Update aliases for pinned receiver variables
|
||||||
|
// When a variable like "me" is pinned to "__pin$N$@recv", both names
|
||||||
|
// must point to the same PHI value to avoid stale ValueId references.
|
||||||
|
// This handles:
|
||||||
|
// 1. Direct receiver: "me" → "__pin$1$@recv"
|
||||||
|
// 2. Nested loops: "__pin$1$@recv" → "__pin$2$@recv"
|
||||||
|
// 3. Multiple aliasing scenarios
|
||||||
|
if pinned.name.contains("@recv") {
|
||||||
|
// Always update "me" (the canonical receiver name)
|
||||||
|
ops.update_var("me".to_string(), pinned.header_phi);
|
||||||
|
|
||||||
|
// Also update all previous pin levels (__pin$1$@recv, __pin$2$@recv, etc.)
|
||||||
|
// Extract the pin counter and update all lower levels
|
||||||
|
if let Some(idx) = pinned.name.find("$") {
|
||||||
|
if let Some(end_idx) = pinned.name[idx + 1..].find("$") {
|
||||||
|
if let Ok(counter) = pinned.name[idx + 1..idx + 1 + end_idx].parse::<u32>()
|
||||||
|
{
|
||||||
|
// Update all previous pin levels (1 through counter-1)
|
||||||
|
for i in 1..counter {
|
||||||
|
let alias = format!("__pin${}$@recv", i);
|
||||||
|
ops.update_var(alias, pinned.header_phi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit PHIs for carrier variables
|
||||||
|
for carrier in &builder.carriers {
|
||||||
|
ops.emit_phi(
|
||||||
|
carrier.header_phi,
|
||||||
|
vec![(builder.preheader_id, carrier.preheader_copy)],
|
||||||
|
)?;
|
||||||
|
ops.update_var(carrier.name.clone(), carrier.header_phi);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
276
src/mir/phi_core/loopform/passes/pass4_seal.rs
Normal file
276
src/mir/phi_core/loopform/passes/pass4_seal.rs
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
//! Pass 4: PHI Sealing - Complete PHI Nodes with Latch Values
|
||||||
|
//!
|
||||||
|
//! This pass completes the PHI nodes created in Pass 3 by adding the latch
|
||||||
|
//! input (the backedge from loop body to header). It also handles continue
|
||||||
|
//! statements which create additional predecessors to the header.
|
||||||
|
//!
|
||||||
|
//! # Responsibilities
|
||||||
|
//!
|
||||||
|
//! 1. **Latch Value Discovery**: Find the final value of each variable at the latch
|
||||||
|
//! 2. **PHI Input Completion**: Update incomplete PHI nodes with:
|
||||||
|
//! - Preheader input (already set in Pass 3)
|
||||||
|
//! - Continue inputs (from continue statements)
|
||||||
|
//! - Latch input (backedge)
|
||||||
|
//! 3. **PHI Optimization**: Skip PHI update if all inputs have same value
|
||||||
|
//! 4. **Header Bypass**: Skip PHI updates in JoinIR experimental path
|
||||||
|
//!
|
||||||
|
//! # Algorithm
|
||||||
|
//!
|
||||||
|
//! For each variable (pinned and carrier):
|
||||||
|
//! 1. Collect all PHI inputs:
|
||||||
|
//! - Preheader: `preheader_copy`
|
||||||
|
//! - Continues: Variable values at continue blocks
|
||||||
|
//! - Latch: Variable value at latch block
|
||||||
|
//! 2. Sanitize inputs: Remove duplicates using BTreeMap
|
||||||
|
//! 3. Optimize: If all values are same, skip PHI update
|
||||||
|
//! 4. Update: Call `update_phi_inputs()` with complete input list
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! // After Pass 3 (incomplete PHI):
|
||||||
|
//! // r4 = phi [r3, @preheader] // carrier "x"
|
||||||
|
//!
|
||||||
|
//! // Loop body execution:
|
||||||
|
//! // @body:
|
||||||
|
//! // r10 = r4 + 1 // x = x + 1
|
||||||
|
//! // jump @latch
|
||||||
|
//! // @latch:
|
||||||
|
//! // jump @header // backedge
|
||||||
|
//!
|
||||||
|
//! builder.seal_phis(&mut ops, latch_id, &[], &HashSet::new(), false)?;
|
||||||
|
//!
|
||||||
|
//! // After Pass 4 (complete PHI):
|
||||||
|
//! // r4 = phi [r3, @preheader], [r10, @latch]
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::{BasicBlockId, ValueId};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::mir::phi_core::loopform::builder_core::{LoopFormBuilder, LoopFormOps};
|
||||||
|
use crate::mir::phi_core::loopform::utils::is_loopform_debug_enabled;
|
||||||
|
|
||||||
|
/// Pass 4: Seal PHI nodes after loop body lowering
|
||||||
|
///
|
||||||
|
/// Completes PHI nodes with latch + continue inputs, converting them from:
|
||||||
|
/// phi [preheader_val, preheader]
|
||||||
|
/// to:
|
||||||
|
/// phi [preheader_val, preheader], [continue_val, continue_bb]..., [latch_val, latch]
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `latch_id`: The block that closes the canonical backedge to `header`.
|
||||||
|
/// - `continue_snapshots`: Per-`continue` block variable snapshots.
|
||||||
|
/// Each entry represents a predecessor of `header` created by `continue`.
|
||||||
|
/// - `_writes`: Variables modified in loop body (Step 5-1: 選択肢2)
|
||||||
|
/// Used to distinguish true carriers from loop-invariant variables
|
||||||
|
/// (Currently unused - PHI optimization uses optimize_same_value() instead)
|
||||||
|
/// - `header_bypass`: Phase 27.4C: Header φ バイパスフラグ
|
||||||
|
/// true の場合、Header φ 生成がスキップされているため、φ lookup も行わない
|
||||||
|
pub fn seal_phis<O: LoopFormOps>(
|
||||||
|
builder: &mut LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
latch_id: BasicBlockId,
|
||||||
|
continue_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
|
_writes: &std::collections::HashSet<String>,
|
||||||
|
header_bypass: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
crate::runtime::get_global_ring0().log.debug(&format!(
|
||||||
|
"[loopform/seal_phis] header={:?} preheader={:?} latch={:?} continue_snapshots={}",
|
||||||
|
builder.header_id,
|
||||||
|
builder.preheader_id,
|
||||||
|
latch_id,
|
||||||
|
continue_snapshots.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 27.4C Refactor: Delegate to specialized methods
|
||||||
|
seal_pinned_phis(builder, ops, latch_id, continue_snapshots, header_bypass)?;
|
||||||
|
seal_carrier_phis(builder, ops, latch_id, continue_snapshots, header_bypass)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.4C Refactor: Seal Pinned 変数 PHIs
|
||||||
|
///
|
||||||
|
/// Pinned 変数(ループ不変パラメータ)の PHI ノード入力を finalize する。
|
||||||
|
/// Header φ バイパス時は φ lookup をスキップし、preheader_copy をそのまま使用。
|
||||||
|
fn seal_pinned_phis<O: LoopFormOps>(
|
||||||
|
builder: &LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
latch_id: BasicBlockId,
|
||||||
|
continue_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
|
header_bypass: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let debug = is_loopform_debug_enabled();
|
||||||
|
|
||||||
|
for pinned in &builder.pinned {
|
||||||
|
if header_bypass {
|
||||||
|
// Phase 27.4C: JoinIR 実験経路では Pinned 変数の φ lookup をスキップ
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis/27.4C] SKIP pinned '{}' phi update (header bypass active)",
|
||||||
|
pinned.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Phase 59: PhiInputCollector インライン化
|
||||||
|
// ========================================
|
||||||
|
// Step 1: 入力収集
|
||||||
|
let mut raw_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||||
|
raw_inputs.push((builder.preheader_id, pinned.preheader_copy));
|
||||||
|
|
||||||
|
for (cid, snapshot) in continue_snapshots {
|
||||||
|
if let Some(&value) = snapshot.get(&pinned.name) {
|
||||||
|
raw_inputs.push((*cid, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let latch_value = ops
|
||||||
|
.get_variable_at_block(&pinned.name, latch_id)
|
||||||
|
.unwrap_or(pinned.header_phi);
|
||||||
|
raw_inputs.push((latch_id, latch_value));
|
||||||
|
|
||||||
|
// Step 2: sanitize (BTreeMapで重複削除&ソート)
|
||||||
|
let mut sanitized: std::collections::BTreeMap<BasicBlockId, ValueId> =
|
||||||
|
std::collections::BTreeMap::new();
|
||||||
|
for (bb, val) in &raw_inputs {
|
||||||
|
sanitized.insert(*bb, *val);
|
||||||
|
}
|
||||||
|
let inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect();
|
||||||
|
|
||||||
|
// Step 3: optimize_same_value
|
||||||
|
let same_value = if inputs.is_empty() {
|
||||||
|
None
|
||||||
|
} else if inputs.len() == 1 {
|
||||||
|
Some(inputs[0].1)
|
||||||
|
} else {
|
||||||
|
let first_val = inputs[0].1;
|
||||||
|
if inputs.iter().all(|(_, val)| *val == first_val) {
|
||||||
|
Some(first_val)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(same_val) = same_value {
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis] OPTIMIZED pinned '{}': phi={:?} → same_value={:?} (loop-invariant)",
|
||||||
|
pinned.name, pinned.header_phi, same_val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis] pinned '{}' phi={:?} inputs={:?}",
|
||||||
|
pinned.name, pinned.header_phi, inputs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.update_phi_inputs(builder.header_id, pinned.header_phi, inputs)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.4C Refactor: Seal Carrier 変数 PHIs
|
||||||
|
///
|
||||||
|
/// Carrier 変数(ループ内変数)の PHI ノード入力を finalize する。
|
||||||
|
/// Header φ バイパス時は φ lookup をスキップし、preheader_copy をそのまま使用。
|
||||||
|
fn seal_carrier_phis<O: LoopFormOps>(
|
||||||
|
builder: &mut LoopFormBuilder,
|
||||||
|
ops: &mut O,
|
||||||
|
latch_id: BasicBlockId,
|
||||||
|
continue_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||||
|
header_bypass: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let debug = is_loopform_debug_enabled();
|
||||||
|
|
||||||
|
for carrier in &mut builder.carriers {
|
||||||
|
if header_bypass {
|
||||||
|
// Phase 27.4C: JoinIR 実験経路では Carrier 変数の φ lookup をスキップ
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis/27.4C] SKIP carrier '{}' phi update (header bypass active)",
|
||||||
|
carrier.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
carrier.latch_value = ops
|
||||||
|
.get_variable_at_block(&carrier.name, latch_id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"carrier '{}' not found at latch {:?}",
|
||||||
|
carrier.name, latch_id
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Phase 59: PhiInputCollector インライン化
|
||||||
|
// ========================================
|
||||||
|
// Step 1: 入力収集
|
||||||
|
let mut raw_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||||
|
raw_inputs.push((builder.preheader_id, carrier.preheader_copy));
|
||||||
|
|
||||||
|
for (cid, snapshot) in continue_snapshots {
|
||||||
|
if let Some(&value) = snapshot.get(&carrier.name) {
|
||||||
|
raw_inputs.push((*cid, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_inputs.push((latch_id, carrier.latch_value));
|
||||||
|
|
||||||
|
// Step 2: sanitize (BTreeMapで重複削除&ソート)
|
||||||
|
let mut sanitized: std::collections::BTreeMap<BasicBlockId, ValueId> =
|
||||||
|
std::collections::BTreeMap::new();
|
||||||
|
for (bb, val) in &raw_inputs {
|
||||||
|
sanitized.insert(*bb, *val);
|
||||||
|
}
|
||||||
|
let inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect();
|
||||||
|
|
||||||
|
// Step 3: optimize_same_value
|
||||||
|
let same_value = if inputs.is_empty() {
|
||||||
|
None
|
||||||
|
} else if inputs.len() == 1 {
|
||||||
|
Some(inputs[0].1)
|
||||||
|
} else {
|
||||||
|
let first_val = inputs[0].1;
|
||||||
|
if inputs.iter().all(|(_, val)| *val == first_val) {
|
||||||
|
Some(first_val)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(same_val) = same_value {
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis] OPTIMIZED carrier '{}': phi={:?} → same_value={:?} (misclassified as carrier, actually loop-invariant)",
|
||||||
|
carrier.name, carrier.header_phi, same_val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[loopform/seal_phis] carrier '{}' phi={:?} inputs={:?}",
|
||||||
|
carrier.name, carrier.header_phi, inputs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.update_phi_inputs(builder.header_id, carrier.header_phi, inputs)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -5,14 +5,14 @@
|
|||||||
//! - Bypass flag management
|
//! - Bypass flag management
|
||||||
//! - Environment variable handling
|
//! - Environment variable handling
|
||||||
|
|
||||||
use super::loopform_variable_models::LoopBypassFlags;
|
use super::variable_models::LoopBypassFlags;
|
||||||
|
|
||||||
/// Phase 27.4C Cleanup: Check if LoopForm debug logging is enabled
|
/// Phase 27.4C Cleanup: Check if LoopForm debug logging is enabled
|
||||||
///
|
///
|
||||||
/// Returns true if the `NYASH_LOOPFORM_DEBUG` environment variable is set.
|
/// Returns true if the `NYASH_LOOPFORM_DEBUG` environment variable is set.
|
||||||
/// This helper avoids duplicate checks across multiple locations.
|
/// This helper avoids duplicate checks across multiple locations.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn is_loopform_debug_enabled() -> bool {
|
pub fn is_loopform_debug_enabled() -> bool {
|
||||||
std::env::var("NYASH_LOOPFORM_DEBUG").is_ok()
|
std::env::var("NYASH_LOOPFORM_DEBUG").is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ pub(crate) fn is_loopform_debug_enabled() -> bool {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// - `LoopBypassFlags` - Header/Exit bypass status
|
/// - `LoopBypassFlags` - Header/Exit bypass status
|
||||||
pub(crate) fn get_loop_bypass_flags(_fn_name: &str) -> LoopBypassFlags {
|
pub fn get_loop_bypass_flags(_fn_name: &str) -> LoopBypassFlags {
|
||||||
LoopBypassFlags {
|
LoopBypassFlags {
|
||||||
// Phase 73: Header φ bypass experiment discontinued (always OFF).
|
// Phase 73: Header φ bypass experiment discontinued (always OFF).
|
||||||
// Only verified in LoopScopeShape/JoinIR mainline.
|
// Only verified in LoopScopeShape/JoinIR mainline.
|
||||||
@ -42,7 +42,7 @@ pub(crate) fn get_loop_bypass_flags(_fn_name: &str) -> LoopBypassFlags {
|
|||||||
///
|
///
|
||||||
/// Phase 73: Exit φ bypass experiment discontinued (always OFF).
|
/// Phase 73: Exit φ bypass experiment discontinued (always OFF).
|
||||||
/// JoinIR path is verified in LoopScopeShape/Exit φ mainline.
|
/// JoinIR path is verified in LoopScopeShape/Exit φ mainline.
|
||||||
pub(crate) fn joinir_exit_bypass_enabled() -> bool {
|
pub fn joinir_exit_bypass_enabled() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ pub(crate) fn joinir_exit_bypass_enabled() -> bool {
|
|||||||
/// Currently limited to 2 functions:
|
/// Currently limited to 2 functions:
|
||||||
/// - Main.skip/1
|
/// - Main.skip/1
|
||||||
/// - FuncScannerBox.trim/1
|
/// - FuncScannerBox.trim/1
|
||||||
pub(crate) fn is_joinir_exit_bypass_target(func_name: &str) -> bool {
|
pub fn is_joinir_exit_bypass_target(func_name: &str) -> bool {
|
||||||
matches!(func_name, "Main.skip/1" | "FuncScannerBox.trim/1")
|
matches!(func_name, "Main.skip/1" | "FuncScannerBox.trim/1")
|
||||||
}
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,133 +0,0 @@
|
|||||||
//! Phase 191: LoopForm 4-Pass PHI Generation Engine
|
|
||||||
//!
|
|
||||||
//! Responsibility: LoopForm → PHI construction 4-stage pipeline
|
|
||||||
//! 1. prepare_structure(): ValueId pre-allocation and carrier/pinned classification
|
|
||||||
//! 2. emit_preheader(): Preheader copy instruction generation
|
|
||||||
//! 3. emit_header_phis(): Header PHI generation (incomplete)
|
|
||||||
//! 4. seal_phis(): PHI completion (pinned/carrier separation)
|
|
||||||
//!
|
|
||||||
//! Note: This module documents the 4-pass architecture. The actual implementation
|
|
||||||
//! remains in loopform_builder.rs for stability. Future refactoring will move
|
|
||||||
//! the implementation here.
|
|
||||||
|
|
||||||
use crate::mir::{BasicBlockId, ValueId};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
/// 4-Pass PHI generation engine
|
|
||||||
///
|
|
||||||
/// This struct documents the 4-pass pipeline architecture used by LoopFormBuilder.
|
|
||||||
/// Each pass has a specific responsibility:
|
|
||||||
///
|
|
||||||
/// ## Pass 1: prepare_structure()
|
|
||||||
/// - Classify variables as carriers (modified in loop) or pinned (loop-invariant)
|
|
||||||
/// - Allocate ValueIds upfront for preheader_copy and header_phi
|
|
||||||
/// - Ensure counter is after all existing ValueIds
|
|
||||||
///
|
|
||||||
/// ## Pass 2: emit_preheader()
|
|
||||||
/// - Generate copy instructions in preheader block
|
|
||||||
/// - Order: pinned variables first, then carrier variables (deterministic)
|
|
||||||
///
|
|
||||||
/// ## Pass 3: emit_header_phis()
|
|
||||||
/// - Generate incomplete PHI nodes in header block
|
|
||||||
/// - First input: preheader_copy (known)
|
|
||||||
/// - Second input: latch value (unknown at this point)
|
|
||||||
///
|
|
||||||
/// ## Pass 4: seal_phis()
|
|
||||||
/// - Complete PHI nodes by finding latch values
|
|
||||||
/// - Separate handling for pinned (seal_pinned_phis) and carrier (seal_carrier_phis)
|
|
||||||
/// - Update PHI inputs with actual latch values
|
|
||||||
///
|
|
||||||
/// # Example Usage
|
|
||||||
/// ```ignore
|
|
||||||
/// let mut builder = LoopFormBuilder::new(preheader, header);
|
|
||||||
/// builder.prepare_structure(&mut ops, ¤t_vars)?;
|
|
||||||
/// builder.emit_preheader(&mut ops)?;
|
|
||||||
/// builder.emit_header_phis(&mut ops)?;
|
|
||||||
/// // ... loop body construction ...
|
|
||||||
/// builder.seal_phis(&mut ops, &latch_vars)?;
|
|
||||||
/// ```
|
|
||||||
pub struct LoopFormPassEngine {
|
|
||||||
// Future: Will contain state for pass management
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoopFormPassEngine {
|
|
||||||
/// Create a new pass engine
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run all 4 passes (placeholder)
|
|
||||||
///
|
|
||||||
/// This is a placeholder for future implementation.
|
|
||||||
/// Currently, the passes are called directly on LoopFormBuilder.
|
|
||||||
pub fn run_all_passes(&mut self) -> Result<(), String> {
|
|
||||||
// Future implementation
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pass metadata for documentation and future optimization
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PassMetadata {
|
|
||||||
/// Pass number (1-4)
|
|
||||||
pub pass_number: u8,
|
|
||||||
/// Pass name
|
|
||||||
pub name: &'static str,
|
|
||||||
/// Pass description
|
|
||||||
pub description: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get metadata for all 4 passes
|
|
||||||
pub fn get_pass_metadata() -> [PassMetadata; 4] {
|
|
||||||
[
|
|
||||||
PassMetadata {
|
|
||||||
pass_number: 1,
|
|
||||||
name: "prepare_structure",
|
|
||||||
description: "ValueId allocation and variable classification",
|
|
||||||
},
|
|
||||||
PassMetadata {
|
|
||||||
pass_number: 2,
|
|
||||||
name: "emit_preheader",
|
|
||||||
description: "Preheader copy instruction generation",
|
|
||||||
},
|
|
||||||
PassMetadata {
|
|
||||||
pass_number: 3,
|
|
||||||
name: "emit_header_phis",
|
|
||||||
description: "Incomplete header PHI generation",
|
|
||||||
},
|
|
||||||
PassMetadata {
|
|
||||||
pass_number: 4,
|
|
||||||
name: "seal_phis",
|
|
||||||
description: "PHI completion with latch values",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pass_engine_creation() {
|
|
||||||
let engine = LoopFormPassEngine::new();
|
|
||||||
// Just verify it compiles
|
|
||||||
let _ = engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pass_metadata() {
|
|
||||||
let metadata = get_pass_metadata();
|
|
||||||
assert_eq!(metadata.len(), 4);
|
|
||||||
assert_eq!(metadata[0].pass_number, 1);
|
|
||||||
assert_eq!(metadata[0].name, "prepare_structure");
|
|
||||||
assert_eq!(metadata[1].name, "emit_preheader");
|
|
||||||
assert_eq!(metadata[2].name, "emit_header_phis");
|
|
||||||
assert_eq!(metadata[3].name, "seal_phis");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pass_engine_run_all_passes() {
|
|
||||||
let mut engine = LoopFormPassEngine::new();
|
|
||||||
assert!(engine.run_all_passes().is_ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -13,13 +13,8 @@ pub mod conservative;
|
|||||||
// Phase 30 F-2.1: loop_phi 削除(LoopFormBuilder が SSOT)
|
// Phase 30 F-2.1: loop_phi 削除(LoopFormBuilder が SSOT)
|
||||||
pub mod loop_snapshot_merge;
|
pub mod loop_snapshot_merge;
|
||||||
|
|
||||||
// Phase 191: LoopForm modularization
|
// Phase 191-193: LoopForm modularization - complete directory structure
|
||||||
pub mod loopform_context;
|
pub mod loopform;
|
||||||
pub mod loopform_variable_models;
|
|
||||||
pub mod loopform_utils;
|
|
||||||
pub mod loopform_passes;
|
|
||||||
pub mod loopform_exit_phi;
|
|
||||||
pub mod loopform_builder;
|
|
||||||
// Trio legacy boxes removed in Phase 70: LoopScopeShape now owns classification/liveness.
|
// Trio legacy boxes removed in Phase 70: LoopScopeShape now owns classification/liveness.
|
||||||
|
|
||||||
// Phase 26-B: Box-First Refactoring
|
// Phase 26-B: Box-First Refactoring
|
||||||
|
|||||||
@ -24,7 +24,7 @@ use super::super::ast::ExprV0;
|
|||||||
use super::super::ast::StmtV0;
|
use super::super::ast::StmtV0;
|
||||||
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||||
use crate::ast::Span;
|
use crate::ast::Span;
|
||||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
use crate::mir::phi_core::loopform::{LoopFormBuilder, LoopFormOps};
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user