diff --git a/src/mir/phi_core/loopform_builder.rs b/src/mir/phi_core/loopform_builder.rs index fd182906..81a36938 100644 --- a/src/mir/phi_core/loopform_builder.rs +++ b/src/mir/phi_core/loopform_builder.rs @@ -6,6 +6,13 @@ * * Phase: 25.1b prototype implementation → 25.1m で安定化 * Status: Always-on(LoopForm PHI v2 は既定実装。NYASH_LOOPFORM_PHI_V2 は互換目的のみで、挙動切り替えには使われない) + * + * Phase 191: Modularized into separate submodules + * - loopform_context: ValueId management + * - loopform_variable_models: CarrierVariable, PinnedVariable types + * - loopform_utils: Debug and bypass utilities + * - loopform_passes: 4-pass architecture documentation + * - loopform_exit_phi: Exit PHI builder */ use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox; @@ -14,126 +21,21 @@ use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox; use crate::mir::{BasicBlockId, ValueId}; use std::collections::BTreeMap; -/// Phase 27.4C Cleanup: LoopForm デバッグログが有効かチェック -/// -/// 環境変数 `NYASH_LOOPFORM_DEBUG` が設定されている場合に true を返す。 -/// 複数箇所での重複チェックを避けるためのヘルパー関数。 -#[inline] -pub(crate) fn is_loopform_debug_enabled() -> bool { - std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() -} +// Phase 191: Import modularized components +pub use super::loopform_context::LoopFormContext; +pub use super::loopform_variable_models::{CarrierVariable, PinnedVariable, LoopBypassFlags}; -// ============================================================================ -// Phase 30 F-2.1: JoinIR バイパスフラグ(header_phi_builder から移動) -// ============================================================================ +// Import utility functions (keeping them pub(crate) as they were) +use super::loopform_utils::{ + is_loopform_debug_enabled, get_loop_bypass_flags, + joinir_exit_bypass_enabled, is_joinir_exit_bypass_target +}; -// Phase 185: JOINIR_HEADER_BYPASS_TARGETS and is_joinir_header_bypass_target() removed -// (Unused legacy code from Phase 27.4-C, superseded by JOINIR_TARGETS in targets.rs) - -/// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグ統合 -/// -/// Header φ と Exit φ のバイパスフラグを一箇所で管理。 -/// 将来的に Exit φ バイパス(Phase 27.6-2)とも統合可能。 -#[derive(Debug, Clone, Copy, Default)] -pub(crate) struct LoopBypassFlags { - /// Header φ バイパスが有効か - pub header: bool, - // Phase 30: exit フィールド削除(完全未使用、将来 JoinIR で代替予定) -} - -/// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグを取得 -/// -/// **用途**: Header/Exit φ バイパスの判定を一元化。 -/// 複数箇所での重複したトグル判定を避ける。 -/// -/// # Arguments -/// - `fn_name` - 関数名(例: "Main.skip/1") -/// -/// # Returns -/// - `LoopBypassFlags` - Header/Exit バイパスの有効状態 -pub(crate) fn get_loop_bypass_flags(fn_name: &str) -> LoopBypassFlags { - LoopBypassFlags { - // Phase 73: Header φ バイパス実験は廃止(常時 OFF)。 - // LoopScopeShape/JoinIR 本線でのみ検証。 - header: false, - } -} - -// ============================================================================ -// Phase 30 F-2.1: Exit バイパスフラグ(exit_phi_builder から移動) -// ============================================================================ - -/// JoinIR Exit φ バイパスが有効かどうか -/// -/// - NYASH_JOINIR_EXPERIMENT=1 -/// - (legacy) Exit φ バイパス用の実験フラグ -/// -/// の両方が立っているときだけ true。 -pub(crate) fn joinir_exit_bypass_enabled() -> bool { - // Phase 73: Exit φ バイパス実験は廃止(常時 OFF)。JoinIR 経路は LoopScopeShape/Exit φ 本線で検証。 - false -} - -/// Exit φ バイパス対象の関数かどうか -/// -/// 当面は minimal/trim の 2 本だけ: -/// - Main.skip/1 -/// - FuncScannerBox.trim/1 -pub(crate) fn is_joinir_exit_bypass_target(func_name: &str) -> bool { - matches!(func_name, "Main.skip/1" | "FuncScannerBox.trim/1") -} - -/// 📦 LoopForm Context - Box-First理論に基づくパラメータ予約明示化 -/// -/// ValueId割り当ての境界を明確にし、パラメータ予約を明示的に管理。 -/// 「境界をはっきりさせる」→「差し替え可能にする」原則を実現。 -#[derive(Debug, Clone)] -pub struct LoopFormContext { - /// 次に割り当てる ValueId - pub next_value_id: u32, - /// パラメータ数(予約済み ValueId 数) - pub param_count: usize, -} - -impl LoopFormContext { - /// パラメータ数と既存変数から context を作成 - pub fn new(param_count: usize, existing_vars: &BTreeMap) -> Self { - // パラメータ予約を考慮した最小値を計算 - let min_from_params = param_count as u32; - let min_from_vars = existing_vars.values().map(|v| v.0 + 1).max().unwrap_or(0); - - Self { - next_value_id: min_from_params.max(min_from_vars), - param_count, - } - } - - /// 既存の ValueId の後に counter を設定 - pub fn ensure_after(&mut self, max_id: u32) { - if self.next_value_id <= max_id { - self.next_value_id = max_id + 1; - } - } -} - -/// A carrier variable: modified within the loop (loop-carried dependency) -#[derive(Debug, Clone)] -pub struct CarrierVariable { - pub name: String, - pub init_value: ValueId, // Initial value from preheader (local variable) - pub preheader_copy: ValueId, // Copy allocated in preheader block - pub header_phi: ValueId, // PHI node allocated in header block - pub latch_value: ValueId, // Updated value computed in latch (set during sealing) -} - -/// A pinned variable: not modified in loop body (loop-invariant, typically parameters) -#[derive(Debug, Clone)] -pub struct PinnedVariable { - pub name: String, - pub param_value: ValueId, // Original parameter or loop-invariant value - pub preheader_copy: ValueId, // Copy allocated in preheader block - pub header_phi: ValueId, // PHI node allocated in header block -} +// Phase 191: Utility functions moved to loopform_utils.rs +// Phase 191: LoopFormContext moved to loopform_context.rs +// Phase 191: CarrierVariable and PinnedVariable moved to loopform_variable_models.rs +// Phase 191: LoopBypassFlags moved to loopform_variable_models.rs +// All re-exported above for compatibility /// LoopForm Meta-Box: Structured representation of loop SSA construction /// @@ -910,6 +812,8 @@ pub trait LoopFormOps { /// /// **Important**: This does NOT change any PHI generation logic - it merely /// provides a ControlForm-based entry point while preserving existing behavior. +// Phase 191: build_exit_phis_for_control moved to loopform_exit_phi.rs +// Re-exported here for compatibility pub fn build_exit_phis_for_control( loopform: &LoopFormBuilder, ops: &mut O, @@ -918,32 +822,16 @@ pub fn build_exit_phis_for_control( crate::mir::BasicBlockId, std::collections::BTreeMap, )], - // Existing implementation requires branch_source_block, so we accept it as a parameter branch_source_block: crate::mir::BasicBlockId, ) -> Result<(), String> { - use crate::mir::control_form::ControlKind; - - // Only process Loop structures; silently succeed for other control kinds - let shape = match &form.kind { - ControlKind::Loop(shape) => shape, - _ => return Ok(()), - }; - - // Extract exit_id from LoopShape - let exit_id = shape.exit; - - // Log ControlForm usage when trace is enabled - let trace = std::env::var("NYASH_LOOPFORM_DEBUG").ok().is_some(); - if trace { - eprintln!( - "[loopform/exit-phi/control-form] Using ControlForm wrapper: exit={:?} branch_source={:?}", - exit_id, branch_source_block - ); - } - - // Phase 69-2: LocalScopeInspectorBox 完全削除 - // variable_definitions は LoopScopeShape に移行済み(Phase 48-4) - loopform.build_exit_phis(ops, exit_id, branch_source_block, exit_snapshots) + use super::loopform_exit_phi::ExitPhiBuilder; + ExitPhiBuilder::build_exit_phis_for_control( + loopform, + ops, + form, + exit_snapshots, + branch_source_block, + ) } #[cfg(test)] diff --git a/src/mir/phi_core/loopform_context.rs b/src/mir/phi_core/loopform_context.rs new file mode 100644 index 00000000..f39871df --- /dev/null +++ b/src/mir/phi_core/loopform_context.rs @@ -0,0 +1,148 @@ +//! Phase 191: LoopForm Context - ValueId Management +//! +//! Responsibility: +//! - Track next available ValueId +//! - Parameter reservation +//! - Counter initialization +//! +//! Box-First principle: Make boundaries explicit for ValueId allocation, +//! enabling substitution and clear separation of concerns. + +use crate::mir::ValueId; +use std::collections::BTreeMap; + +/// LoopForm PHI generation context +/// +/// Manages ValueId allocation for PHI construction, providing a clear boundary +/// for value numbering. This is the foundation for PHI building. +#[derive(Debug, Clone)] +pub struct LoopFormContext { + /// Next ValueId to allocate + pub next_value_id: u32, + /// Parameter count (number of reserved ValueIds) + pub param_count: usize, +} + +impl LoopFormContext { + /// Create a new context from parameter count and existing variables + /// + /// # Arguments + /// * `param_count` - Number of function parameters + /// * `existing_vars` - Existing variables (local variables, etc.) + /// + /// # Returns + /// A new context with next_value_id set after parameters and existing variables + pub fn new(param_count: usize, existing_vars: &BTreeMap) -> Self { + // Reserve space for parameters + let min_from_params = param_count as u32; + // Find the highest existing ValueId + let min_from_vars = existing_vars.values().map(|v| v.0 + 1).max().unwrap_or(0); + + Self { + next_value_id: min_from_params.max(min_from_vars), + param_count, + } + } + + /// Ensure next_value_id is after the given max_id + /// + /// Used to avoid conflicts with ValueIds allocated outside this context. + pub fn ensure_after(&mut self, max_id: u32) { + if self.next_value_id <= max_id { + self.next_value_id = max_id + 1; + } + } + + /// Get next ValueId and increment counter + pub fn next_value(&mut self) -> ValueId { + let id = ValueId(self.next_value_id); + self.next_value_id += 1; + id + } + + /// Allocate multiple ValueIds at once + /// + /// # Arguments + /// * `count` - Number of ValueIds to allocate + /// + /// # Returns + /// A vector of allocated ValueIds + pub fn alloc_multiple(&mut self, count: usize) -> Vec { + (0..count).map(|_| self.next_value()).collect() + } + + /// Peek at the next ValueId without consuming it + pub fn peek(&self) -> ValueId { + ValueId(self.next_value_id) + } + + /// Get the current counter value (raw u32) + pub fn current_counter(&self) -> u32 { + self.next_value_id + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_context_new_empty() { + let ctx = LoopFormContext::new(0, &BTreeMap::new()); + assert_eq!(ctx.peek(), ValueId(0)); + assert_eq!(ctx.param_count, 0); + } + + #[test] + fn test_context_new_with_params() { + let ctx = LoopFormContext::new(2, &BTreeMap::new()); + assert_eq!(ctx.peek(), ValueId(2)); + assert_eq!(ctx.param_count, 2); + } + + #[test] + fn test_context_new_with_existing_vars() { + let mut vars = BTreeMap::new(); + vars.insert("x".to_string(), ValueId(3)); + vars.insert("y".to_string(), ValueId(5)); + + let ctx = LoopFormContext::new(2, &vars); + // Should start after the highest existing var (5 + 1 = 6) + assert_eq!(ctx.peek(), ValueId(6)); + } + + #[test] + fn test_context_next_value() { + let mut ctx = LoopFormContext::new(0, &BTreeMap::new()); + assert_eq!(ctx.next_value(), ValueId(0)); + assert_eq!(ctx.next_value(), ValueId(1)); + assert_eq!(ctx.next_value(), ValueId(2)); + } + + #[test] + fn test_context_alloc_multiple() { + let mut ctx = LoopFormContext::new(0, &BTreeMap::new()); + let ids = ctx.alloc_multiple(3); + assert_eq!(ids, vec![ValueId(0), ValueId(1), ValueId(2)]); + assert_eq!(ctx.peek(), ValueId(3)); + } + + #[test] + fn test_context_ensure_after() { + let mut ctx = LoopFormContext::new(0, &BTreeMap::new()); + assert_eq!(ctx.peek(), ValueId(0)); + + ctx.ensure_after(10); + assert_eq!(ctx.peek(), ValueId(11)); + + // Should not go backwards + ctx.ensure_after(5); + assert_eq!(ctx.peek(), ValueId(11)); + } + + #[test] + fn test_context_current_counter() { + let ctx = LoopFormContext::new(3, &BTreeMap::new()); + assert_eq!(ctx.current_counter(), 3); + } +} diff --git a/src/mir/phi_core/loopform_exit_phi.rs b/src/mir/phi_core/loopform_exit_phi.rs new file mode 100644 index 00000000..18c1f828 --- /dev/null +++ b/src/mir/phi_core/loopform_exit_phi.rs @@ -0,0 +1,96 @@ +//! Phase 191: LoopForm Exit PHI Builder +//! +//! Responsibility: Exit PHI generation (break/continue merge handling) +//! - Exit block value merging +//! - Pinned/Carrier variable classification +//! - Collaboration with loop_snapshot_merge.rs +//! +//! This module extracts the exit PHI building logic from loopform_builder.rs +//! to improve modularity and testability. + +use crate::mir::{BasicBlockId, ValueId}; +use crate::mir::control_form::ControlForm; +use std::collections::BTreeMap; + +/// Exit PHI builder (currently a delegation wrapper) +/// +/// Future: Will contain the extracted build_exit_phis logic +pub struct ExitPhiBuilder; + +impl ExitPhiBuilder { + /// Build exit PHIs using LoopFormBuilder delegation + /// + /// This is a temporary implementation that delegates to the existing + /// build_exit_phis method in LoopFormBuilder. Future refactoring will + /// move the actual implementation here. + /// + /// # Arguments + /// * `loopform` - The LoopFormBuilder containing carrier/pinned metadata + /// * `ops` - Operations trait for MIR emission + /// * `exit_id` - Exit block ID + /// * `branch_source_block` - Branch source block + /// * `exit_snapshots` - Snapshots from each exit predecessor + pub fn build_exit_phis( + loopform: &super::loopform_builder::LoopFormBuilder, + ops: &mut O, + exit_id: BasicBlockId, + branch_source_block: BasicBlockId, + exit_snapshots: &[(BasicBlockId, BTreeMap)], + ) -> Result<(), String> { + // Delegate to existing implementation + loopform.build_exit_phis(ops, exit_id, branch_source_block, exit_snapshots) + } + + /// Build exit PHIs for ControlForm (adapter) + /// + /// This adapter extracts the LoopShape from ControlForm and calls build_exit_phis. + /// This is the interface used by the loop lowering code. + /// + /// # Arguments + /// * `loopform` - The LoopFormBuilder + /// * `ops` - Operations trait + /// * `form` - ControlForm containing loop structure + /// * `exit_snapshots` - Snapshots from exit predecessors + /// * `branch_source_block` - Branch source block + pub fn build_exit_phis_for_control( + loopform: &super::loopform_builder::LoopFormBuilder, + ops: &mut O, + form: &ControlForm, + exit_snapshots: &[(BasicBlockId, BTreeMap)], + branch_source_block: BasicBlockId, + ) -> Result<(), String> { + use crate::mir::control_form::ControlKind; + + // Only process Loop structures; silently succeed for other control kinds + let shape = match &form.kind { + ControlKind::Loop(shape) => shape, + _ => return Ok(()), + }; + + // Extract exit_id from LoopShape + let exit_id = shape.exit; + + // Log ControlForm usage when trace is enabled + let trace = std::env::var("NYASH_LOOPFORM_DEBUG").ok().is_some(); + if trace { + eprintln!( + "[loopform/exit-phi/control-form] Using ControlForm wrapper: exit={:?} branch_source={:?}", + exit_id, branch_source_block + ); + } + + // Delegate to the main build_exit_phis + Self::build_exit_phis(loopform, ops, exit_id, branch_source_block, exit_snapshots) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_exit_phi_builder_exists() { + // ExitPhiBuilder is stateless, so just check it exists + let _ = ExitPhiBuilder; + } +} diff --git a/src/mir/phi_core/loopform_passes.rs b/src/mir/phi_core/loopform_passes.rs new file mode 100644 index 00000000..08d8f1ff --- /dev/null +++ b/src/mir/phi_core/loopform_passes.rs @@ -0,0 +1,133 @@ +//! 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()); + } +} diff --git a/src/mir/phi_core/loopform_utils.rs b/src/mir/phi_core/loopform_utils.rs new file mode 100644 index 00000000..67e73b6c --- /dev/null +++ b/src/mir/phi_core/loopform_utils.rs @@ -0,0 +1,112 @@ +//! Phase 191: LoopForm Utilities +//! +//! Responsibility: Debug and bypass-related utility functions +//! - Debug logging control +//! - Bypass flag management +//! - Environment variable handling + +use super::loopform_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 { + std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() +} + +/// Phase 27.4-C Refactor: Get JoinIR Loop φ bypass flags +/// +/// **Purpose**: Centralize Header/Exit φ bypass determination. +/// Avoids duplicate toggle checks across multiple locations. +/// +/// # Arguments +/// - `fn_name` - Function name (e.g., "Main.skip/1") +/// +/// # Returns +/// - `LoopBypassFlags` - Header/Exit bypass status +pub(crate) fn get_loop_bypass_flags(_fn_name: &str) -> LoopBypassFlags { + LoopBypassFlags { + // Phase 73: Header φ bypass experiment discontinued (always OFF). + // Only verified in LoopScopeShape/JoinIR mainline. + header: false, + } +} + +/// Check if JoinIR Exit φ bypass is enabled +/// +/// Returns true only when both of the following are set: +/// - NYASH_JOINIR_EXPERIMENT=1 +/// - (legacy) Exit φ bypass experimental flag +/// +/// Phase 73: Exit φ bypass experiment discontinued (always OFF). +/// JoinIR path is verified in LoopScopeShape/Exit φ mainline. +pub(crate) fn joinir_exit_bypass_enabled() -> bool { + false +} + +/// Check if a function is a JoinIR Exit φ bypass target +/// +/// Currently limited to 2 functions: +/// - Main.skip/1 +/// - FuncScannerBox.trim/1 +pub(crate) fn is_joinir_exit_bypass_target(func_name: &str) -> bool { + matches!(func_name, "Main.skip/1" | "FuncScannerBox.trim/1") +} + +/// Load bypass flags from environment variables +/// +/// Returns (skip_pinned, skip_carrier, skip_exit) tuple +pub fn load_bypass_flags_from_env() -> (bool, bool, bool) { + let skip_pinned = std::env::var("NYASH_BYPASS_PINNED_PHI") + .map(|v| v == "1") + .unwrap_or(false); + let skip_carrier = std::env::var("NYASH_BYPASS_CARRIER_PHI") + .map(|v| v == "1") + .unwrap_or(false); + let skip_exit = std::env::var("NYASH_BYPASS_EXIT_PHI") + .map(|v| v == "1") + .unwrap_or(false); + (skip_pinned, skip_carrier, skip_exit) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_loopform_debug_enabled() { + // Should return false if env var not set (default case) + // Note: Actual value depends on environment, so we just check it compiles + let _ = is_loopform_debug_enabled(); + } + + #[test] + fn test_get_loop_bypass_flags() { + let flags = get_loop_bypass_flags("Main.skip/1"); + // Phase 73: Always disabled + assert!(!flags.header); + } + + #[test] + fn test_joinir_exit_bypass_enabled() { + // Phase 73: Always disabled + assert!(!joinir_exit_bypass_enabled()); + } + + #[test] + fn test_is_joinir_exit_bypass_target() { + assert!(is_joinir_exit_bypass_target("Main.skip/1")); + assert!(is_joinir_exit_bypass_target("FuncScannerBox.trim/1")); + assert!(!is_joinir_exit_bypass_target("Other.function/1")); + } + + #[test] + fn test_load_bypass_flags_default() { + // Environment variables may or may not be set, so we just verify it works + let (skip_p, skip_c, skip_e) = load_bypass_flags_from_env(); + // Just ensure it returns booleans + let _ = (skip_p, skip_c, skip_e); + } +} diff --git a/src/mir/phi_core/loopform_variable_models.rs b/src/mir/phi_core/loopform_variable_models.rs new file mode 100644 index 00000000..2b184f27 --- /dev/null +++ b/src/mir/phi_core/loopform_variable_models.rs @@ -0,0 +1,101 @@ +//! Phase 191: LoopForm Variable Models +//! +//! Responsibility: Type definitions for LoopForm variable representations +//! - CarrierVariable: Loop-carried dependencies (modified in loop) +//! - PinnedVariable: Loop-invariant parameters +//! - LoopBypassFlags: Bypass control flags + +use crate::mir::ValueId; + +/// A carrier variable: modified within the loop (loop-carried dependency) +/// +/// Represents a variable that is updated in the loop body. +/// Flow: init_value → preheader_copy → header_phi → latch_value +#[derive(Debug, Clone)] +pub struct CarrierVariable { + pub name: String, + pub init_value: ValueId, // Initial value from preheader (local variable) + pub preheader_copy: ValueId, // Copy allocated in preheader block + pub header_phi: ValueId, // PHI node allocated in header block + pub latch_value: ValueId, // Updated value computed in latch (set during sealing) +} + +/// A pinned variable: not modified in loop body (loop-invariant, typically parameters) +/// +/// Represents a variable that remains constant throughout loop iterations. +/// Flow: param_value → preheader_copy → header_phi (with same value) +#[derive(Debug, Clone)] +pub struct PinnedVariable { + pub name: String, + pub param_value: ValueId, // Original parameter or loop-invariant value + pub preheader_copy: ValueId, // Copy allocated in preheader block + pub header_phi: ValueId, // PHI node allocated in header block +} + +/// Phase 27.4-C Refactor: JoinIR Loop φ bypass flags +/// +/// Controls bypass behavior for Header and Exit PHI nodes. +/// Future: Will be integrated with JoinIR Exit φ bypass (Phase 27.6-2). +#[derive(Debug, Clone, Copy, Default)] +pub struct LoopBypassFlags { + /// Header φ bypass enabled + pub header: bool, + // Phase 30: exit field removed (completely unused, will be replaced by JoinIR) +} + +impl LoopBypassFlags { + /// Create new bypass flags with all disabled + pub fn new() -> Self { + Self::default() + } + + /// Check if all bypass flags are enabled + pub fn all_enabled(&self) -> bool { + // Currently only header flag exists + self.header + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bypass_flags_default() { + let flags = LoopBypassFlags::new(); + assert!(!flags.all_enabled()); + assert!(!flags.header); + } + + #[test] + fn test_bypass_flags_all_enabled() { + let mut flags = LoopBypassFlags::new(); + flags.header = true; + assert!(flags.all_enabled()); + } + + #[test] + fn test_carrier_variable_creation() { + let carrier = CarrierVariable { + name: "x".to_string(), + init_value: ValueId(0), + preheader_copy: ValueId(1), + header_phi: ValueId(2), + latch_value: ValueId(3), + }; + assert_eq!(carrier.name, "x"); + assert_eq!(carrier.init_value.0, 0); + } + + #[test] + fn test_pinned_variable_creation() { + let pinned = PinnedVariable { + name: "param".to_string(), + param_value: ValueId(0), + preheader_copy: ValueId(1), + header_phi: ValueId(2), + }; + assert_eq!(pinned.name, "param"); + assert_eq!(pinned.param_value.0, 0); + } +} diff --git a/src/mir/phi_core/mod.rs b/src/mir/phi_core/mod.rs index e50c947f..a3b5d629 100644 --- a/src/mir/phi_core/mod.rs +++ b/src/mir/phi_core/mod.rs @@ -12,6 +12,13 @@ pub mod conservative; // Phase 84-5: if_phi 削除(レガシーフォールバック完全削除) // 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; // Trio legacy boxes removed in Phase 70: LoopScopeShape now owns classification/liveness.