Files
hakorune/src/mir/join_ir/lowering/loop_to_join.rs

439 lines
17 KiB
Rust
Raw Normal View History

//! 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<JoinModule> {
if self.debug {
eprintln!(
"[LoopToJoinLowerer] lower() called for {:?}",
func_name.unwrap_or("<unknown>")
);
}
// 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("<unknown>"),
loop_id,
region.header,
exit_edges.iter().map(|e| e.to).collect::<Vec<_>>()
);
}
// Phase 32 L-1.4: ExitAnalysis ベースの Case-A サポートチェック
if !self.is_supported_case_a_loop_view(func, &region, &control, &exit_edges, &scope) {
if self.debug {
eprintln!(
"[LoopToJoinLowerer] rejected by view-based check: {:?}",
func_name.unwrap_or("<unknown>")
);
}
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("<unknown>")
);
}
return None;
}
} else if self.debug {
eprintln!(
"[LoopToJoinLowerer] generic Case-A enabled, allowing {:?}",
func_name.unwrap_or("<unknown>")
);
}
// 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`: LoopScopeShapeprogress_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(&region.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<JoinModule> {
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<JoinModule> {
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<JoinModule> {
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<JoinModule> {
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<JoinModule> {
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<JoinModule> {
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<JoinModule> {
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
}
}