diff --git a/src/mir/join_ir/lowering/common.rs b/src/mir/join_ir/lowering/common.rs index 0ded46e0..b8034827 100644 --- a/src/mir/join_ir/lowering/common.rs +++ b/src/mir/join_ir/lowering/common.rs @@ -4,9 +4,58 @@ pub mod case_a; +use crate::mir::loop_form::LoopForm; use crate::mir::query::{MirQuery, MirQueryBox}; use crate::mir::{BasicBlockId, BinaryOp, ConstValue, MirInstruction}; +// ============================================================================ +// Phase 32: LoopForm construction helpers +// ============================================================================ + +/// 単純な while ループの LoopForm を CFG から構築する +/// +/// # Arguments +/// - `entry`: 関数エントリブロック +/// - `query`: MIR クエリ +/// - `entry_is_preheader`: true なら entry を preheader として使う(trim 用) +/// false なら entry の succ を preheader とする(stage1 用) +/// - `has_break`: true なら exit を break_targets に含める +/// +/// # Loop structure assumed +/// ```text +/// [entry] → [preheader] → [header] ─┬→ [body] → [latch] → [header] +/// └→ [exit] +/// ``` +/// +/// Note: latch は body と同じブロックとして扱う(is_simple_case_a_loop 対応) +pub fn construct_simple_while_loopform( + entry: BasicBlockId, + query: &MirQueryBox, + entry_is_preheader: bool, + has_break: bool, +) -> Option { + let preheader = if entry_is_preheader { + entry + } else { + query.succs(entry).get(0).copied()? + }; + + let header = query.succs(preheader).get(0).copied().unwrap_or(preheader); + let succs_header = query.succs(header); + let body = succs_header.get(0).copied().unwrap_or(header); + let exit = succs_header.get(1).copied().unwrap_or(header); + + Some(LoopForm { + preheader, + header, + body, + latch: body, // is_simple_case_a_loop 対応: latch == body + exit, + continue_targets: vec![body], + break_targets: if has_break { vec![exit] } else { vec![] }, + }) +} + /// Check if entry block has at least one successor /// /// Returns `true` if the entry block has at least one successor, `false` otherwise. diff --git a/src/mir/join_ir/lowering/funcscanner_trim.rs b/src/mir/join_ir/lowering/funcscanner_trim.rs index 2156140f..87840008 100644 --- a/src/mir/join_ir/lowering/funcscanner_trim.rs +++ b/src/mir/join_ir/lowering/funcscanner_trim.rs @@ -620,21 +620,15 @@ fn lower_trim_from_mir(module: &crate::mir::MirModule) -> Option { eprintln!("[joinir/trim/mir] CFG sanity checks passed ✅"); // Phase 31: LoopToJoinLowerer 統一箱経由に移行 + // Phase 32: construct_simple_while_loopform 共通ヘルパーを使用 if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") { + use crate::mir::join_ir::lowering::common::construct_simple_while_loopform; use crate::mir::join_ir::lowering::loop_to_join::LoopToJoinLowerer; - 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); - let exit = succs_header.get(1).copied().unwrap_or(header); - let loop_form = LoopForm { - preheader: entry_id, - header, - body, - latch: body, - exit, - continue_targets: vec![body], - break_targets: vec![exit], + // trim: entry_is_preheader=true, has_break=true + let Some(loop_form) = construct_simple_while_loopform(entry_id, &query, true, true) else { + eprintln!("[joinir/trim/generic-hook] failed to construct LoopForm from CFG"); + return build_funcscanner_trim_joinir(module); }; if crate::mir::join_ir::lowering::common::case_a::is_simple_case_a_loop(&loop_form) { eprintln!( diff --git a/src/mir/join_ir/lowering/loop_to_join.rs b/src/mir/join_ir/lowering/loop_to_join.rs index 29308ffe..40323534 100644 --- a/src/mir/join_ir/lowering/loop_to_join.rs +++ b/src/mir/join_ir/lowering/loop_to_join.rs @@ -15,7 +15,7 @@ //! let join_module = lowerer.lower(func, &loop_form, &query)?; //! ``` -use crate::mir::control_form::LoopId; +use crate::mir::control_form::{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; @@ -26,6 +26,14 @@ 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 で導入された統一インターフェース。 @@ -75,18 +83,7 @@ impl LoopToJoinLowerer { ); } - // Phase 31 Step 3-A: 早期リターン(関数名フィルタ) - // Note: scope 構築前でもチェック可能(現在は func_name のみで判定) - // Step 3-B で構造チェックを追加予定(その時は scope 構築後に移動) - if !func_name.map_or(false, super::loop_scope_shape::is_case_a_minimal_target) { - if self.debug { - eprintln!( - "[LoopToJoinLowerer] early return: {:?} is not a Case-A minimal target", - func_name.unwrap_or("") - ); - } - return None; - } + // Phase 32 L-1.2: 早期フィルタは削除。構造チェック後に条件付きで適用する。 // Step 1: MirQuery を構築 let query = MirQueryBox::new(func); @@ -109,20 +106,51 @@ impl LoopToJoinLowerer { ); } - // Phase 32 Step 3-A: View メソッドを使った構造確認(段階移行) - if self.debug { - 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); - let continue_edges = loop_form.to_continue_edges(loop_id); + // 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: region.header={:?}, control.exits={}, exit_edges={}, continue_edges={}", + "[LoopToJoinLowerer] Phase 32 views: func={:?} loop_id={:?} header={:?} exits={:?}", + func_name.unwrap_or(""), + loop_id, region.header, - control.exits.len(), - exit_edges.len(), - continue_edges.len() + exit_edges.iter().map(|e| e.to).collect::>() + ); + } + + // Phase 32 L-1.1: View ベースの Case-A サポートチェック(構造判定強化) + if !self.is_supported_case_a_loop_view(func, ®ion, &control, &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("") ); } @@ -130,51 +158,77 @@ impl LoopToJoinLowerer { self.lower_with_scope(scope, func_name) } - /// Case-A ループとしてサポートされているかチェック + /// Case-A ループとしてサポートされているかチェック(View ベース版) /// - /// Phase 31 Step 3-B: 関数名フィルタ + 構造チェック + /// Phase 32 L-1.2: 純粋な構造チェックのみ(関数名フィルタは lower() 側で適用) /// /// # Case-A の定義 /// - /// - 単一出口(break_targets が 1 箇所以下) + /// - 単一出口(exits が 1 箇所以下) + /// - ヘッダブロックの succ が 2 つ(cond true/false) /// - ループ変数または固定変数が存在 /// - ネストループなし(将来チェック追加予定) /// /// # Arguments /// - /// - `loop_form`: LoopForm(構造判定用) + /// - `func`: MIR 関数(ヘッダ succ チェック用) + /// - `region`: LoopRegion(ブロック構造) + /// - `control`: LoopControlShape(制御フロー辺) /// - `scope`: LoopScopeShape(変数分類用) - /// - `func_name`: 関数名(Step 3-C まで限定フィルタ) /// /// # Returns /// /// - `true`: Case-A として lowering 可能 /// - `false`: 未サポート(フォールバック経路へ) - #[allow(dead_code)] // Step 3-C/D で使用予定 - pub(crate) fn is_supported_case_a_loop( + fn is_supported_case_a_loop_view( &self, - loop_form: &LoopForm, + func: &MirFunction, + region: &LoopRegion, + control: &LoopControlShape, scope: &LoopScopeShape, - func_name: Option<&str>, ) -> bool { - // Phase 31 Step 3-A: 関数名フィルタ(Step 3-D で撤去予定) - if !func_name.map_or(false, super::loop_scope_shape::is_case_a_minimal_target) { - return false; - } + // Phase 32 L-1.2: 純粋な構造チェックのみ + // 関数名フィルタは lower() 側で generic_case_a_enabled() に応じて適用 - // Phase 31 Step 3-B: 構造チェック(追加) - // 単一出口 (break が 1 箇所以下) - if loop_form.break_targets.len() > 1 { + // 1) 単一出口 (Case-A 限定: exits が 1 箇所以下) + // Note: break_targets ベースなので、条件 false による自然な出口は含まない + // 将来 Case-B/C で複数出口を許可する場合はここを緩める + if control.exits.len() > 1 { if self.debug { eprintln!( - "[LoopToJoinLowerer] rejected: multiple break targets ({})", - loop_form.break_targets.len() + "[LoopToJoinLowerer] rejected: multiple exits ({}) - Case-A requires single exit", + control.exits.len() ); } return false; } - // ループ変数または固定変数がある(空ループは対象外) + // 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"); @@ -185,10 +239,12 @@ impl LoopToJoinLowerer { true } + // Note: is_supported_case_a_loop (legacy) は Phase 32 で削除済み + // 代替: is_supported_case_a_loop_view() を使用 + /// LoopScopeShape から JoinModule を生成(内部メソッド) /// - /// Phase 31 Step 2: 関数名で 4 パターンにディスパッチ - /// Step 3 で汎用 Case-A lowering に統一予定 + /// Phase 32 L-1.2: 関数名で 4 パターン + 汎用 Case-A にディスパッチ fn lower_with_scope( &self, scope: LoopScopeShape, @@ -196,7 +252,8 @@ impl LoopToJoinLowerer { ) -> Option { let name = func_name.unwrap_or(""); - // Phase 31: matches! で完全一致(is_case_a_minimal_target と同じ条件) + // Phase 32 L-1.2: minimal 4 本にマッチしたらそれぞれの lowerer を使う + // マッチしない場合は汎用 Case-A(まだ未実装、None を返す) let result = match name { "Main.skip/1" => { if self.debug { @@ -223,10 +280,12 @@ impl LoopToJoinLowerer { generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope) } _ => { - // TODO: Phase 31 Step 3 で汎用 Case A 対応 + // Phase 32 L-1.2: 汎用 Case-A 候補 + // ここに来るのは NYASH_JOINIR_LOWER_GENERIC=1 の時だけ + // TODO: 汎用 Case-A lowerer を実装したらここで呼び出す if self.debug { eprintln!( - "[LoopToJoinLowerer] no matching pattern for {:?}, returning None", + "[LoopToJoinLowerer] generic Case-A candidate: {:?} (no lowerer yet)", name ); } diff --git a/src/mir/join_ir/lowering/stage1_using_resolver.rs b/src/mir/join_ir/lowering/stage1_using_resolver.rs index 3e1303f8..9165b1d8 100644 --- a/src/mir/join_ir/lowering/stage1_using_resolver.rs +++ b/src/mir/join_ir/lowering/stage1_using_resolver.rs @@ -344,22 +344,24 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { eprintln!("[joinir/stage1_using_resolver/mir] CFG sanity checks passed ✅"); // Phase 31: LoopToJoinLowerer 統一箱経由に移行 + // Phase 32 L-2.1: CFG から正確な LoopForm を構築 + // Phase 32: construct_simple_while_loopform 共通ヘルパーを使用 if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") { + use crate::mir::join_ir::lowering::common::construct_simple_while_loopform; use crate::mir::join_ir::lowering::loop_to_join::LoopToJoinLowerer; - 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); - let exit = succs_header.get(1).copied().unwrap_or(header); - let loop_form = LoopForm { - preheader: entry, - header, - body, - latch: body, - exit, - continue_targets: vec![body], - break_targets: vec![exit], + // Stage-1: entry_is_preheader=false (entry の succ が preheader) + // has_break=false (このループに break はない) + let Some(loop_form) = construct_simple_while_loopform(entry, &query, false, false) else { + eprintln!("[joinir/stage1_using_resolver/generic-hook] failed to construct LoopForm from CFG"); + return lower_handwritten(module); }; + + eprintln!( + "[joinir/stage1_using_resolver/generic-hook] constructed LoopForm: preheader={:?} header={:?} body={:?} latch={:?} exit={:?} break={:?}", + loop_form.preheader, loop_form.header, loop_form.body, loop_form.latch, loop_form.exit, loop_form.break_targets + ); + if crate::mir::join_ir::lowering::common::case_a::is_simple_case_a_loop(&loop_form) { eprintln!( "[joinir/stage1_using_resolver/generic-hook] simple Case A loop detected (LoopToJoinLowerer)" @@ -377,6 +379,10 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { eprintln!( "[joinir/stage1_using_resolver/generic-hook] LoopToJoinLowerer returned None or params mismatch, falling back to handwritten/MIR path" ); + } else { + eprintln!( + "[joinir/stage1_using_resolver/generic-hook] NOT simple Case A loop, falling back" + ); } }