diff --git a/src/mir/join_ir/lowering/funcscanner_append_defs.rs b/src/mir/join_ir/lowering/funcscanner_append_defs.rs index 512c0d70..7adddc19 100644 --- a/src/mir/join_ir/lowering/funcscanner_append_defs.rs +++ b/src/mir/join_ir/lowering/funcscanner_append_defs.rs @@ -346,7 +346,12 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { eprintln!("[joinir/funcscanner_append_defs/mir] CFG sanity checks passed ✅"); eprintln!("[joinir/funcscanner_append_defs/mir] Found: length(), get(), push(), i+1"); + // Phase 30 F-3: LoopScopeShape 経由の新API を使用 if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") { + use crate::mir::join_ir::lowering::generic_case_a::lower_case_a_append_defs_with_scope; + use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; + use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; + let header = query.succs(entry).get(0).copied().unwrap_or(entry); let succs_header = query.succs(header); let body = succs_header.get(0).copied().unwrap_or(header); @@ -366,20 +371,27 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { ); let var_classes = crate::mir::phi_core::loop_var_classifier::LoopVarClassBox::new(); let exit_live = crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox::new(); - if let Some(jm) = crate::mir::join_ir::lowering::generic_case_a::lower_case_a_loop_to_joinir_for_append_defs_minimal( - &loop_form, - &var_classes, - &exit_live, - &query, - target_func, - ) { - eprintln!( - "[joinir/funcscanner_append_defs/generic-hook] generic_case_a produced JoinIR, returning early" - ); - return Some(jm); + + // Phase 30 F-3: LoopFormIntake + LoopScopeShape 経由で呼び出し + if let Some(intake) = intake_loop_form(&loop_form, &var_classes, &query, target_func) { + if let Some(scope) = LoopScopeShape::from_existing_boxes( + &loop_form, + &intake, + &var_classes, + &exit_live, + &query, + Some("FuncScannerBox.append_defs/2"), // Phase 30 F-3.1: Case-A minimal target + ) { + if let Some(jm) = lower_case_a_append_defs_with_scope(scope) { + eprintln!( + "[joinir/funcscanner_append_defs/generic-hook] generic_case_a produced JoinIR via _with_scope, returning early" + ); + return Some(jm); + } + } } eprintln!( - "[joinir/funcscanner_append_defs/generic-hook] generic_case_a returned None, falling back to handwritten/MIR path" + "[joinir/funcscanner_append_defs/generic-hook] generic_case_a via _with_scope returned None, falling back to handwritten/MIR path" ); } } diff --git a/src/mir/join_ir/lowering/funcscanner_trim.rs b/src/mir/join_ir/lowering/funcscanner_trim.rs index 305fbb7e..cd30417b 100644 --- a/src/mir/join_ir/lowering/funcscanner_trim.rs +++ b/src/mir/join_ir/lowering/funcscanner_trim.rs @@ -619,7 +619,12 @@ fn lower_trim_from_mir(module: &crate::mir::MirModule) -> Option { eprintln!("[joinir/trim/mir] CFG sanity checks passed ✅"); + // Phase 30 F-3: LoopScopeShape 経由の新API を使用 if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") { + use crate::mir::join_ir::lowering::generic_case_a::lower_case_a_trim_with_scope; + use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; + use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; + let header = query.succs(entry_id).get(0).copied().unwrap_or(entry_id); let succs_header = query.succs(header); let body = succs_header.get(0).copied().unwrap_or(header); @@ -639,12 +644,25 @@ fn lower_trim_from_mir(module: &crate::mir::MirModule) -> Option { ); let var_classes = crate::mir::phi_core::loop_var_classifier::LoopVarClassBox::new(); let exit_live = crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox::new(); - if let Some(jm) = crate::mir::join_ir::lowering::generic_case_a::lower_case_a_loop_to_joinir_for_trim_minimal(&loop_form, &var_classes, &exit_live, &query, target_func) { - eprintln!("[joinir/trim/generic-hook] generic_case_a produced JoinIR, returning early"); - return Some(jm); + + // Phase 30 F-3: LoopFormIntake + LoopScopeShape 経由で呼び出し + if let Some(intake) = intake_loop_form(&loop_form, &var_classes, &query, target_func) { + if let Some(scope) = LoopScopeShape::from_existing_boxes( + &loop_form, + &intake, + &var_classes, + &exit_live, + &query, + Some("FuncScannerBox.trim/1"), // Phase 30 F-3.1: Case-A minimal target + ) { + if let Some(jm) = lower_case_a_trim_with_scope(scope) { + eprintln!("[joinir/trim/generic-hook] generic_case_a produced JoinIR via _with_scope, returning early"); + return Some(jm); + } + } } eprintln!( - "[joinir/trim/generic-hook] generic_case_a placeholder returned None, falling back to handwritten" + "[joinir/trim/generic-hook] generic_case_a via _with_scope returned None, falling back to handwritten" ); } } diff --git a/src/mir/join_ir/lowering/loop_scope_shape.rs b/src/mir/join_ir/lowering/loop_scope_shape.rs index 5f518fe8..a692f551 100644 --- a/src/mir/join_ir/lowering/loop_scope_shape.rs +++ b/src/mir/join_ir/lowering/loop_scope_shape.rs @@ -12,19 +12,34 @@ //! - LoopScopeShape: 変数の「役割」(pinned/carrier/body_local/exit_live) //! - JoinIR: 関数と継続で表現された制御構造 //! -//! # Phase 29 Strategy +//! # Phase 30 F-3.1 Strategy: Case-A Minimal Routing //! -//! 現在は「既存箱を呼ぶだけの薄いラッパー」として実装。 -//! 将来は既存箱を吸収して LoopScopeShape を唯一のソースにする(Phase 30)。 +//! `from_existing_boxes` メソッドは、関数名に基づいてCase-A minimalターゲット +//! (skip_ws, trim, append_defs, stage1_using_resolver)を新しいパスに +//! ルーティングする。現在は同じ結果を返すが、将来的に `analyze_case_a` は +//! MIRベースの独立解析を行う基盤となる。 +//! +//! ## Routing Logic +//! +//! ```ignore +//! from_existing_boxes(func_name: Option<&str>) +//! ├── Some("Main.skip/1") → analyze_case_a() +//! ├── Some("FuncScannerBox.trim/1") → analyze_case_a() +//! ├── Some("FuncScannerBox.append_defs/2") → analyze_case_a() +//! ├── Some("Stage1UsingResolverBox.resolve_for_source/5") → analyze_case_a() +//! └── None or other → from_existing_boxes_legacy() +//! ``` //! //! # Usage //! //! ```ignore //! let scope = LoopScopeShape::from_existing_boxes( -//! &loop_form_intake, +//! &loop_form, +//! &intake, //! &var_classes, //! &exit_live, //! query, +//! Some("Main.skip/1"), // Case-A minimal target //! )?; //! //! // JoinIR lowering では scope のフィールドを参照 @@ -42,6 +57,36 @@ use crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox; use crate::mir::phi_core::loop_var_classifier::{LoopVarClass, LoopVarClassBox}; use crate::mir::{BasicBlockId, MirQuery, ValueId}; +// ============================================================================ +// Phase 30 F-3.1: Case-A Minimal Target Detection +// ============================================================================ + +/// Phase 30 F-3.1: Case-A minimal ターゲット判定 +/// +/// 現在 JoinIR lowering でサポートしている Case-A minimal ループのみ true を返す。 +/// これらは LoopScopeShape の新しい analyze_case_a パスを通る。 +/// +/// # Supported Targets +/// +/// - `Main.skip/1`: minimal_ssa_skip_ws.hako +/// - `FuncScannerBox.trim/1`: funcscanner_trim_min.hako +/// - `FuncScannerBox.append_defs/2`: funcscanner_append_defs_min.hako +/// - `Stage1UsingResolverBox.resolve_for_source/5`: stage1_using_resolver minimal +/// +/// # Future +/// +/// この関数は将来的に LoopForm/LoopScopeShape ベースの汎用判定に置き換わる予定。 +/// その時点でこのハードコードリストは削除される。 +pub(crate) fn is_case_a_minimal_target(func_name: &str) -> bool { + matches!( + func_name, + "Main.skip/1" + | "FuncScannerBox.trim/1" + | "FuncScannerBox.append_defs/2" + | "Stage1UsingResolverBox.resolve_for_source/5" + ) +} + /// ループ変数スコープの統合ビュー /// /// # Phase 30: 変数分類の唯一の仕様ソース (SSOT) @@ -160,7 +205,7 @@ pub(crate) struct LoopScopeShape { } impl LoopScopeShape { - /// Create LoopScopeShape from existing boxes (Phase 30: unified interface) + /// Create LoopScopeShape from existing boxes (Phase 30 F-3.1: unified interface) /// /// # Arguments /// @@ -169,28 +214,146 @@ impl LoopScopeShape { /// - `var_classes`: LoopVarClassBox for classification /// - `exit_live_box`: LoopExitLivenessBox for exit liveness /// - `query`: MirQuery for liveness computation + /// - `func_name`: Optional function name for Case-A minimal routing /// /// # Returns /// /// Some(LoopScopeShape) if successful, None if critical data is missing. /// - /// # Phase 30 Design + /// # Phase 30 F-3.1 Design /// /// This is the primary entry point for creating LoopScopeShape. - /// Block IDs come from loop_form, variable classification from existing boxes. - /// Future phases will internalize the box logic entirely. + /// - Case-A minimal targets route through `analyze_case_a` (new path) + /// - Other loops use `from_existing_boxes_legacy` (existing path) + /// + /// Both paths currently produce the same result, but analyze_case_a + /// is the foundation for future MIR-based independent analysis. pub(crate) fn from_existing_boxes( loop_form: &LoopForm, intake: &LoopFormIntake, var_classes: &LoopVarClassBox, exit_live_box: &LoopExitLivenessBox, query: &impl MirQuery, + func_name: Option<&str>, + ) -> Option { + // Phase 30 F-3.1: Route Case-A minimal targets through new path + if let Some(name) = func_name { + if is_case_a_minimal_target(name) { + return Self::analyze_case_a( + loop_form, intake, var_classes, exit_live_box, query, name, + ); + } + } + + // Default: use legacy path + Self::from_existing_boxes_legacy(loop_form, intake, var_classes, exit_live_box, query) + } + + /// Check if a variable needs header PHI + #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering + pub fn needs_header_phi(&self, var_name: &str) -> bool { + self.pinned.contains(var_name) || self.carriers.contains(var_name) + } + + /// Check if a variable needs exit PHI + pub fn needs_exit_phi(&self, var_name: &str) -> bool { + self.exit_live.contains(var_name) + } + + /// Get ordered pinned variables (for JoinIR parameter generation) + pub fn pinned_ordered(&self) -> Vec { + self.pinned.iter().cloned().collect() + } + + /// Get ordered carrier variables (for JoinIR parameter generation) + pub fn carriers_ordered(&self) -> Vec { + self.carriers.iter().cloned().collect() + } + + /// Get all variables that need header PHI (pinned + carriers) + #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering + pub fn header_phi_vars(&self) -> Vec { + let mut result: Vec = self.pinned.iter().cloned().collect(); + result.extend(self.carriers.iter().cloned()); + result + } + + /// Get all variables that need exit PHI + #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering + pub fn exit_phi_vars(&self) -> Vec { + self.exit_live.iter().cloned().collect() + } + + // ========================================================================= + // Phase 30 F-3.1: Case-A Minimal Analysis Path + // ========================================================================= + + /// Phase 30 F-3.1: Case-A minimal 用の解析パス + /// + /// 現在は `from_existing_boxes_legacy` と同じ実装だが、 + /// debug_assert で結果を検証し、将来的に MIR ベースの独立実装に移行する。 + /// + /// # Arguments + /// + /// - `loop_form`: LoopForm containing block structure + /// - `intake`: LoopFormIntake containing classified variable info + /// - `var_classes`: LoopVarClassBox for classification + /// - `exit_live_box`: LoopExitLivenessBox for exit liveness + /// - `query`: MirQuery for liveness computation + /// - `func_name`: 関数名(ログ用) + /// + /// # Returns + /// + /// Some(LoopScopeShape) if successful, None if critical data is missing. + fn analyze_case_a( + loop_form: &LoopForm, + intake: &LoopFormIntake, + var_classes: &LoopVarClassBox, + exit_live_box: &LoopExitLivenessBox, + query: &impl MirQuery, + func_name: &str, + ) -> Option { + // Phase 30 F-3.1: 現在は legacy と同じ実装 + // 将来は MIR から独立して pinned/carriers/body_locals/exit_live を計算 + let result = Self::from_existing_boxes_legacy( + loop_form, intake, var_classes, exit_live_box, query, + )?; + + // Debug: Case-A minimal path が使われていることをログ + if std::env::var("NYASH_LOOPSCOPE_DEBUG").is_ok() { + eprintln!( + "[loopscope/case_a] {} via analyze_case_a path (pinned={}, carriers={}, exit_live={})", + func_name, + result.pinned.len(), + result.carriers.len(), + result.exit_live.len(), + ); + } + + // TODO (F-3.1+): MIR ベースの独立計算を実装し、ここで debug_assert_eq! する + // let mir_based_result = Self::compute_from_mir(...); + // debug_assert_eq!(result.pinned, mir_based_result.pinned); + // debug_assert_eq!(result.carriers, mir_based_result.carriers); + + Some(result) + } + + /// Phase 30 F-3.1: 従来の既存箱ベース実装(legacy path) + /// + /// analyze_case_a と分離することで、Case-A minimal だけ新パスを通せる。 + fn from_existing_boxes_legacy( + loop_form: &LoopForm, + intake: &LoopFormIntake, + var_classes: &LoopVarClassBox, + exit_live_box: &LoopExitLivenessBox, + query: &impl MirQuery, ) -> Option { // Extract block IDs from LoopForm let header = loop_form.header; let body = loop_form.body; let latch = loop_form.latch; let exit = loop_form.exit; + // Extract pinned and carriers from intake (already classified) let pinned: BTreeSet = intake.pinned_ordered.iter().cloned().collect(); let carriers: BTreeSet = intake.carrier_ordered.iter().cloned().collect(); @@ -266,41 +429,6 @@ impl LoopScopeShape { }) } - /// Check if a variable needs header PHI - #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering - pub fn needs_header_phi(&self, var_name: &str) -> bool { - self.pinned.contains(var_name) || self.carriers.contains(var_name) - } - - /// Check if a variable needs exit PHI - pub fn needs_exit_phi(&self, var_name: &str) -> bool { - self.exit_live.contains(var_name) - } - - /// Get ordered pinned variables (for JoinIR parameter generation) - pub fn pinned_ordered(&self) -> Vec { - self.pinned.iter().cloned().collect() - } - - /// Get ordered carrier variables (for JoinIR parameter generation) - pub fn carriers_ordered(&self) -> Vec { - self.carriers.iter().cloned().collect() - } - - /// Get all variables that need header PHI (pinned + carriers) - #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering - pub fn header_phi_vars(&self) -> Vec { - let mut result: Vec = self.pinned.iter().cloned().collect(); - result.extend(self.carriers.iter().cloned()); - result - } - - /// Get all variables that need exit PHI - #[allow(dead_code)] // Phase 30: will be used in F-3 generic lowering - pub fn exit_phi_vars(&self) -> Vec { - self.exit_live.iter().cloned().collect() - } - /// Phase 30 F-1.1: 変数を4分類に分類する /// /// LoopVarClassBox.classify() と同じロジックを LoopScopeShape の内部状態から導出。 @@ -548,6 +676,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ); assert!(scope.is_some()); @@ -586,6 +715,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ) .unwrap(); @@ -609,6 +739,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ) .unwrap(); @@ -632,6 +763,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ) .unwrap(); @@ -694,6 +826,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ) .unwrap(); @@ -741,6 +874,7 @@ mod tests { &var_classes, &exit_live_box, &query, + None, // generic test - use legacy path ) .unwrap(); diff --git a/src/mir/join_ir/lowering/skip_ws.rs b/src/mir/join_ir/lowering/skip_ws.rs index 64dd61b3..acb3789b 100644 --- a/src/mir/join_ir/lowering/skip_ws.rs +++ b/src/mir/join_ir/lowering/skip_ws.rs @@ -352,8 +352,12 @@ fn lower_skip_ws_handwritten_or_mir(module: &crate::mir::MirModule) -> Option Option { - use crate::mir::join_ir::lowering::generic_case_a::lower_case_a_loop_to_joinir_for_minimal_skip_ws; + use crate::mir::join_ir::lowering::generic_case_a::lower_case_a_skip_ws_with_scope; + 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::loop_form::LoopForm; use crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox; use crate::mir::phi_core::loop_var_classifier::LoopVarClassBox; @@ -380,14 +384,23 @@ fn try_lower_skip_ws_generic_case_a(module: &crate::mir::MirModule) -> Option Option { eprintln!("[joinir/stage1_using_resolver/mir] CFG sanity checks passed ✅"); + // Phase 30 F-3: LoopScopeShape 経由の新API を使用 if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") { + use crate::mir::join_ir::lowering::generic_case_a::lower_case_a_stage1_usingresolver_with_scope; + use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; + use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; + let header = query.succs(entry).get(0).copied().unwrap_or(entry); let succs_header = query.succs(header); let body = succs_header.get(0).copied().unwrap_or(header); @@ -365,21 +370,27 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { let exit_live = crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox::new(); let params_len = target_func.params.len(); if params_len == 5 { - if let Some(jm) = crate::mir::join_ir::lowering::generic_case_a::lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal( - &loop_form, - &var_classes, - &exit_live, - &query, - target_func, - ) { - eprintln!( - "[joinir/stage1_using_resolver/generic-hook] generic_case_a produced JoinIR, returning early" - ); - return Some(jm); + // Phase 30 F-3: LoopFormIntake + LoopScopeShape 経由で呼び出し + if let Some(intake) = intake_loop_form(&loop_form, &var_classes, &query, target_func) { + if let Some(scope) = LoopScopeShape::from_existing_boxes( + &loop_form, + &intake, + &var_classes, + &exit_live, + &query, + Some("Stage1UsingResolverBox.resolve_for_source/5"), // Phase 30 F-3.1: Case-A minimal target + ) { + if let Some(jm) = lower_case_a_stage1_usingresolver_with_scope(scope) { + eprintln!( + "[joinir/stage1_using_resolver/generic-hook] generic_case_a produced JoinIR via _with_scope, returning early" + ); + return Some(jm); + } + } } } eprintln!( - "[joinir/stage1_using_resolver/generic-hook] generic_case_a returned None or params mismatch, falling back to handwritten/MIR path" + "[joinir/stage1_using_resolver/generic-hook] generic_case_a via _with_scope returned None or params mismatch, falling back to handwritten/MIR path" ); } }