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:
nyash-codex
2025-12-05 21:58:54 +09:00
parent 89e138a2f8
commit 020fbc6740
15 changed files with 1200 additions and 1316 deletions

View 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 classes25.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;
}

View File

@ -30,8 +30,8 @@ impl ExitPhiBuilder {
/// * `exit_id` - Exit block ID
/// * `branch_source_block` - Branch source block
/// * `exit_snapshots` - Snapshots from each exit predecessor
pub fn build_exit_phis<O: super::loopform_builder::LoopFormOps>(
loopform: &super::loopform_builder::LoopFormBuilder,
pub fn build_exit_phis<O: super::builder_core::LoopFormOps>(
loopform: &super::builder_core::LoopFormBuilder,
ops: &mut O,
exit_id: BasicBlockId,
branch_source_block: BasicBlockId,
@ -52,8 +52,8 @@ impl ExitPhiBuilder {
/// * `form` - ControlForm containing loop structure
/// * `exit_snapshots` - Snapshots from exit predecessors
/// * `branch_source_block` - Branch source block
pub fn build_exit_phis_for_control<O: super::loopform_builder::LoopFormOps>(
loopform: &super::loopform_builder::LoopFormBuilder,
pub fn build_exit_phis_for_control<O: super::builder_core::LoopFormOps>(
loopform: &super::builder_core::LoopFormBuilder,
ops: &mut O,
form: &ControlForm,
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],

View 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, &current_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,
)
}

View 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, &current_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;

View 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, &current_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(())
}

View 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(())
}

View 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(())
}

View 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(())
}

View File

@ -5,14 +5,14 @@
//! - Bypass flag management
//! - 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
///
/// Returns true if the `NYASH_LOOPFORM_DEBUG` environment variable is set.
/// This helper avoids duplicate checks across multiple locations.
#[inline]
pub(crate) fn is_loopform_debug_enabled() -> bool {
pub fn is_loopform_debug_enabled() -> bool {
std::env::var("NYASH_LOOPFORM_DEBUG").is_ok()
}
@ -26,7 +26,7 @@ pub(crate) fn is_loopform_debug_enabled() -> bool {
///
/// # Returns
/// - `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 {
// Phase 73: Header φ bypass experiment discontinued (always OFF).
// 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).
/// JoinIR path is verified in LoopScopeShape/Exit φ mainline.
pub(crate) fn joinir_exit_bypass_enabled() -> bool {
pub fn joinir_exit_bypass_enabled() -> bool {
false
}
@ -51,7 +51,7 @@ pub(crate) fn joinir_exit_bypass_enabled() -> bool {
/// Currently limited to 2 functions:
/// - Main.skip/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")
}

File diff suppressed because it is too large Load Diff

View File

@ -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, &current_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());
}
}

View File

@ -13,13 +13,8 @@ pub mod conservative;
// Phase 30 F-2.1: loop_phi 削除LoopFormBuilder が SSOT
pub mod loop_snapshot_merge;
// Phase 191: LoopForm modularization
pub mod loopform_context;
pub mod loopform_variable_models;
pub mod loopform_utils;
pub mod loopform_passes;
pub mod loopform_exit_phi;
pub mod loopform_builder;
// Phase 191-193: LoopForm modularization - complete directory structure
pub mod loopform;
// Trio legacy boxes removed in Phase 70: LoopScopeShape now owns classification/liveness.
// Phase 26-B: Box-First Refactoring

View File

@ -24,7 +24,7 @@ use super::super::ast::ExprV0;
use super::super::ast::StmtV0;
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
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 std::collections::BTreeMap;