refactor(phi_core): Phase 191 loopform_builder.rs modularization
- loopform_context.rs: ValueId management (148 lines) - loopform_variable_models.rs: Type definitions (101 lines) - loopform_utils.rs: Utilities (112 lines) - loopform_passes.rs: 4-pass architecture docs (133 lines) - loopform_exit_phi.rs: Exit PHI builder (96 lines) - loopform_builder.rs: Reduced from 1278 → 1166 lines (8.9% reduction) Total new modules: 590 lines Responsibility separation achieved Test visibility improved All tests passing (loop_min_while.hako verified)
This commit is contained in:
@ -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<String, ValueId>) -> 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<O: LoopFormOps>(
|
||||
loopform: &LoopFormBuilder,
|
||||
ops: &mut O,
|
||||
@ -918,32 +822,16 @@ pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
||||
crate::mir::BasicBlockId,
|
||||
std::collections::BTreeMap<String, crate::mir::ValueId>,
|
||||
)],
|
||||
// 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)]
|
||||
|
||||
148
src/mir/phi_core/loopform_context.rs
Normal file
148
src/mir/phi_core/loopform_context.rs
Normal file
@ -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<String, ValueId>) -> 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<ValueId> {
|
||||
(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);
|
||||
}
|
||||
}
|
||||
96
src/mir/phi_core/loopform_exit_phi.rs
Normal file
96
src/mir/phi_core/loopform_exit_phi.rs
Normal file
@ -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<O: super::loopform_builder::LoopFormOps>(
|
||||
loopform: &super::loopform_builder::LoopFormBuilder,
|
||||
ops: &mut O,
|
||||
exit_id: BasicBlockId,
|
||||
branch_source_block: BasicBlockId,
|
||||
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||
) -> 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<O: super::loopform_builder::LoopFormOps>(
|
||||
loopform: &super::loopform_builder::LoopFormBuilder,
|
||||
ops: &mut O,
|
||||
form: &ControlForm,
|
||||
exit_snapshots: &[(BasicBlockId, BTreeMap<String, ValueId>)],
|
||||
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;
|
||||
}
|
||||
}
|
||||
133
src/mir/phi_core/loopform_passes.rs
Normal file
133
src/mir/phi_core/loopform_passes.rs
Normal file
@ -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());
|
||||
}
|
||||
}
|
||||
112
src/mir/phi_core/loopform_utils.rs
Normal file
112
src/mir/phi_core/loopform_utils.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
101
src/mir/phi_core/loopform_variable_models.rs
Normal file
101
src/mir/phi_core/loopform_variable_models.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user