Files
hakorune/src/mir/join_ir/lowering/loop_to_join.rs
nyash-codex 9d769e9217 refactor(joinir): L-2.2 Step-1 - Unify Stage-B lowering via LoopToJoinLowerer
Stage-B minimal JoinIR lowering統一化:

1. Add Case-A helpers to loop_to_join.rs:
   - lower_case_a_for_stageb_body (StageBBodyExtractorBox.build_body_src/2)
   - lower_case_a_for_stageb_funcscanner (StageBFuncScannerBox.scan_all_boxes/1)

2. Update stageb_body.rs and stageb_funcscanner.rs:
   - Replace manual LoopForm construction with construct_simple_while_loopform
   - Route through LoopToJoinLowerer like other Case-A functions
   - Remove unused imports (LoopForm, MirQuery)

Now all 6 JoinIR lowering modules use the same unified pattern:
- skip_ws, trim, append_defs, stage1_resolver, stageb_body, stageb_funcscanner
All route through LoopToJoinLowerer for consistency.

Test results: JSON snapshot 6/6 PASS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 10:47:35 +09:00

374 lines
14 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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::{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.1: View ベースの Case-A サポートチェック(構造判定強化)
if !self.is_supported_case_a_loop_view(func, &region, &control, &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)
}
/// Case-A ループとしてサポートされているかチェックView ベース版)
///
/// Phase 32 L-1.2: 純粋な構造チェックのみ(関数名フィルタは lower() 側で適用)
///
/// # Case-A の定義
///
/// - 単一出口exits が 1 箇所以下)
/// - ヘッダブロックの succ が 2 つcond true/false
/// - ループ変数または固定変数が存在
/// - ネストループなし(将来チェック追加予定)
///
/// # Arguments
///
/// - `func`: MIR 関数(ヘッダ succ チェック用)
/// - `region`: LoopRegionブロック構造
/// - `control`: LoopControlShape制御フロー辺
/// - `scope`: LoopScopeShape変数分類用
///
/// # Returns
///
/// - `true`: Case-A として lowering 可能
/// - `false`: 未サポート(フォールバック経路へ)
fn is_supported_case_a_loop_view(
&self,
func: &MirFunction,
region: &LoopRegion,
control: &LoopControlShape,
scope: &LoopScopeShape,
) -> bool {
// Phase 32 L-1.2: 純粋な構造チェックのみ
// 関数名フィルタは lower() 側で generic_case_a_enabled() に応じて適用
// 1) 単一出口 (Case-A 限定: exits が 1 箇所以下)
// Note: break_targets ベースなので、条件 false による自然な出口は含まない
// 将来 Case-B/C で複数出口を許可する場合はここを緩める
if control.exits.len() > 1 {
if self.debug {
eprintln!(
"[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(&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;
}
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
}
}