//! Phase 31: LoopToJoinLowerer - 統一 Loop→JoinIR 変換箱 //! //! このモジュールは MIR の LoopForm を JoinIR に変換する統一インターフェースを提供する。 //! //! ## 設計思想 //! //! - **単一エントリポイント**: `LoopToJoinLowerer::lower()` ですべてのループを処理 //! - **パターン自動判定**: LoopScopeShape を解析して適切な変換を選択 //! - **既存コード再利用**: generic_case_a の `_with_scope` 関数を内部で呼び出し //! //! ## 使用例 //! //! ```ignore //! let lowerer = LoopToJoinLowerer::new(); //! let join_module = lowerer.lower(func, &loop_form, &query)?; //! ``` use crate::mir::control_form::{analyze_exits, ExitEdge, LoopControlShape, LoopId, LoopRegion}; use crate::mir::join_ir::lowering::generic_case_a; use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::JoinModule; use crate::mir::loop_form::LoopForm; use crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox; use crate::mir::phi_core::loop_var_classifier::LoopVarClassBox; use crate::mir::query::MirQueryBox; use crate::mir::MirFunction; /// Phase 32 L-1.2: 汎用 Case-A lowering が有効かどうか /// /// `NYASH_JOINIR_LOWER_GENERIC=1` の場合、関数名フィルタを外して /// 構造ベースで Case-A ループを拾う。 fn generic_case_a_enabled() -> bool { crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") } /// Loop→JoinIR 変換の統一箱 /// /// Phase 31 で導入された統一インターフェース。 /// 全ての MIR LoopForm を JoinIR に正規化する。 pub struct LoopToJoinLowerer { /// デバッグモード(詳細ログ出力) debug: bool, } impl Default for LoopToJoinLowerer { fn default() -> Self { Self::new() } } impl LoopToJoinLowerer { /// 新しい LoopToJoinLowerer を作成 pub fn new() -> Self { let debug = std::env::var("NYASH_LOOPTOJOIN_DEBUG") .map(|v| v == "1") .unwrap_or(false); Self { debug } } /// MIR LoopForm を JoinIR に変換 /// /// # Arguments /// /// - `func`: MIR 関数 /// - `loop_form`: 変換対象の LoopForm /// - `func_name`: 関数名(オプション、ルーティング用) /// /// # Returns /// /// - `Some(JoinModule)`: 変換成功 /// - `None`: 変換失敗(フォールバック経路へ) pub fn lower( &self, func: &MirFunction, loop_form: &LoopForm, func_name: Option<&str>, ) -> Option { if self.debug { eprintln!( "[LoopToJoinLowerer] lower() called for {:?}", func_name.unwrap_or("") ); } // Phase 32 L-1.2: 早期フィルタは削除。構造チェック後に条件付きで適用する。 // Step 1: MirQuery を構築 let query = MirQueryBox::new(func); // Step 2: 分類箱を構築(Phase 31 では空箱、将来 MIR 解析で埋める) let var_classes = LoopVarClassBox::new(); let exit_live = LoopExitLivenessBox::new(); // Step 3: LoopFormIntake を構築 let intake = intake_loop_form(loop_form, &var_classes, &query, func)?; // Step 4: LoopScopeShape を構築 let scope = LoopScopeShape::from_existing_boxes(loop_form, &intake, &var_classes, &exit_live, &query, func_name)?; if self.debug { eprintln!( "[LoopToJoinLowerer] LoopScopeShape built: pinned={:?}, carriers={:?}, exit_live={:?}", scope.pinned, scope.carriers, scope.exit_live ); } // Phase 32 Step 3-C: View メソッドで構造情報を取得(常に実行) let loop_id = LoopId(0); // 単一ループの場合は 0 let region = loop_form.to_region_view(loop_id); let control = loop_form.to_control_view(loop_id); let exit_edges = loop_form.to_exit_edges(loop_id); // Debug: view ベースの情報をログ if self.debug { eprintln!( "[LoopToJoinLowerer] Phase 32 views: func={:?} loop_id={:?} header={:?} exits={:?}", func_name.unwrap_or(""), loop_id, region.header, exit_edges.iter().map(|e| e.to).collect::>() ); } // Phase 32 L-1.4: ExitAnalysis ベースの Case-A サポートチェック if !self.is_supported_case_a_loop_view(func, ®ion, &control, &exit_edges, &scope) { if self.debug { eprintln!( "[LoopToJoinLowerer] rejected by view-based check: {:?}", func_name.unwrap_or("") ); } return None; } // Phase 32 L-1.2: 環境変数で分岐 // OFF(既定): 従来の minimal 4 本だけ // ON: 構造だけで通す(関数名フィルタを外す) if !generic_case_a_enabled() { if !func_name.map_or(false, super::loop_scope_shape::is_case_a_minimal_target) { if self.debug { eprintln!( "[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}", func_name.unwrap_or("") ); } return None; } } else if self.debug { eprintln!( "[LoopToJoinLowerer] generic Case-A enabled, allowing {:?}", func_name.unwrap_or("") ); } // Step 5: パターンに応じた lowering を実行 self.lower_with_scope(scope, func_name) } /// Phase 29 L-5.3: Progress carrier の安全性をチェック /// /// 無限ループの可能性があるループ(progress carrier が無い、または更新されない) /// を事前に弾く。 /// /// # Phase 1 実装(保守的) /// /// - `scope.progress_carrier.is_some()` をチェック /// - progress_carrier が設定されていればループは進捗すると仮定 /// /// # Phase 2 (future) /// /// - MirQuery で header→latch 間に Add 命令があるかチェック /// - skip_ws verifier のロジックを MIR レベルで簡略化して適用 /// /// # Arguments /// /// - `scope`: LoopScopeShape(progress_carrier 情報を持つ) /// - `func`: MIR 関数(将来の MirQuery 用) /// - `region`: LoopRegion(将来の header→latch チェック用) /// /// # Returns /// /// - `true`: Progress carrier あり(safe) /// - `false`: Progress carrier なし(unsafe、fallback すべき) fn has_safe_progress( scope: &LoopScopeShape, _func: &MirFunction, // Phase 2 で使用予定 _region: &LoopRegion, // Phase 2 で使用予定 ) -> bool { // Phase 1: 保守的チェック // progress_carrier が設定されていれば、ループは進捗すると仮定 // (典型的には 'i' のような loop index) scope.progress_carrier.is_some() } /// Case-A ループとしてサポートされているかチェック(View ベース版) /// /// Phase 32 L-1.4: ExitGroup ベースの判定に強化 /// /// # Case-A の定義 /// /// - **単一出口グループ**: 全ての出口辺が同じターゲットブロックに向かう /// (例: `if c != ' ' && c != '\t' { break }` は複数 ExitEdge だが、 /// 同じ exit ブロックに向かうので Case-A として許可) /// - **非ローカル出口なし**: Return/Throw がない /// - ヘッダブロックの succ が 2 つ(cond true/false) /// - ループ変数または固定変数が存在 /// /// # Arguments /// /// - `func`: MIR 関数(ヘッダ succ チェック用) /// - `region`: LoopRegion(ブロック構造) /// - `control`: LoopControlShape(制御フロー辺) /// - `exit_edges`: ExitEdge のリスト(グループ化分析用) /// - `scope`: LoopScopeShape(変数分類用) /// /// # Returns /// /// - `true`: Case-A として lowering 可能 /// - `false`: 未サポート(フォールバック経路へ) fn is_supported_case_a_loop_view( &self, func: &MirFunction, region: &LoopRegion, _control: &LoopControlShape, // Phase 32 L-1.4: ExitEdge ベースに移行したため未使用 exit_edges: &[ExitEdge], scope: &LoopScopeShape, ) -> bool { // Phase 32 L-1.4: ExitAnalysis ベースの出口判定 let exit_analysis = analyze_exits(exit_edges); // 1) 単一出口グループ + 非ローカル出口なし // 複数の ExitEdge でも、同じターゲットに向かうなら Case-A として許可 if !exit_analysis.is_single_exit_group() { if self.debug { eprintln!( "[LoopToJoinLowerer] rejected: not single exit group (groups={}, nonlocal={})", exit_analysis.loop_exit_groups.len(), exit_analysis.nonlocal_exits.len() ); // 詳細ログ: 各グループのターゲットを出力 for (i, group) in exit_analysis.loop_exit_groups.iter().enumerate() { eprintln!( " group[{}]: target={:?}, edges={}, has_break={}", i, group.target, group.edges.len(), group.has_break ); } } return false; } // Note: control.exits は ExitEdge の数(辺の数)なので、 // 複数でも単一グループなら OK という新しいロジック // 2) ヘッダブロックの succ が 2 つ(cond true → body, cond false → exit) // これにより while(cond) 形式のループのみを対象とする if let Some(header_block) = func.blocks.get(®ion.header) { let succ_count = header_block.successors.len(); if succ_count != 2 { if self.debug { eprintln!( "[LoopToJoinLowerer] rejected: header {:?} has {} successors (expected 2)", region.header, succ_count ); } return false; } } else { // ヘッダブロックが見つからない(異常ケース) if self.debug { eprintln!( "[LoopToJoinLowerer] rejected: header block {:?} not found", region.header ); } return false; } // 3) ループ変数または固定変数がある(空ループは対象外) if scope.carriers.is_empty() && scope.pinned.is_empty() { if self.debug { eprintln!("[LoopToJoinLowerer] rejected: no carriers or pinned vars"); } return false; } // 4) Phase 29 L-5.3: Progress carrier チェック // 無限ループの可能性があるループを事前に弾く if !Self::has_safe_progress(scope, func, region) { if self.debug { eprintln!( "[LoopToJoinLowerer] rejected: no safe progress carrier (progress_carrier={:?})", scope.progress_carrier ); } return false; } true } // Note: is_supported_case_a_loop (legacy) は Phase 32 で削除済み // 代替: is_supported_case_a_loop_view() を使用 /// LoopScopeShape から JoinModule を生成(内部メソッド) /// /// Phase 32 L-1.2: 関数名で 4 パターン + 汎用 Case-A にディスパッチ fn lower_with_scope( &self, scope: LoopScopeShape, func_name: Option<&str>, ) -> Option { let name = func_name.unwrap_or(""); // Phase 32 L-1.2: minimal 4 本にマッチしたらそれぞれの lowerer を使う // マッチしない場合は汎用 Case-A(まだ未実装、None を返す) let result = match name { "Main.skip/1" => { if self.debug { eprintln!("[LoopToJoinLowerer] dispatching to skip_ws lowerer"); } generic_case_a::lower_case_a_skip_ws_with_scope(scope) } "FuncScannerBox.trim/1" => { if self.debug { eprintln!("[LoopToJoinLowerer] dispatching to trim lowerer"); } generic_case_a::lower_case_a_trim_with_scope(scope) } "FuncScannerBox.append_defs/2" => { if self.debug { eprintln!("[LoopToJoinLowerer] dispatching to append_defs lowerer"); } generic_case_a::lower_case_a_append_defs_with_scope(scope) } "Stage1UsingResolverBox.resolve_for_source/5" => { if self.debug { eprintln!("[LoopToJoinLowerer] dispatching to stage1 lowerer"); } generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope) } _ => { // Phase 32 L-1.2: 汎用 Case-A 候補 // ここに来るのは NYASH_JOINIR_LOWER_GENERIC=1 の時だけ // TODO: 汎用 Case-A lowerer を実装したらここで呼び出す if self.debug { eprintln!( "[LoopToJoinLowerer] generic Case-A candidate: {:?} (no lowerer yet)", name ); } None } }; result } // ======================================== // Case-A helpers for specific function patterns // ======================================== /// Case-A 汎用 lowerer の「Main.skip/1 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_skip_ws( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("Main.skip/1")) } /// Case-A 汎用 lowerer の「FuncScannerBox.trim/1 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_trim( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("FuncScannerBox.trim/1")) } /// Case-A 汎用 lowerer の「FuncScannerBox.append_defs/2 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_append_defs( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("FuncScannerBox.append_defs/2")) } /// Case-A 汎用 lowerer の「Stage1UsingResolverBox.resolve_for_source/5 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_stage1_resolver( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("Stage1UsingResolverBox.resolve_for_source/5")) } /// Case-A 汎用 lowerer の「StageBBodyExtractorBox.build_body_src/2 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_stageb_body( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("StageBBodyExtractorBox.build_body_src/2")) } /// Case-A 汎用 lowerer の「StageBFuncScannerBox.scan_all_boxes/1 用」薄いラッパー。 /// 実際のロジックは `lower` に集約されている。 pub fn lower_case_a_for_stageb_funcscanner( &self, func: &MirFunction, loop_form: &LoopForm, ) -> Option { self.lower(func, loop_form, Some("StageBFuncScannerBox.scan_all_boxes/1")) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_lowerer_creation() { let lowerer = LoopToJoinLowerer::new(); assert!(!lowerer.debug || lowerer.debug); // Just check it compiles } }