Phase 32 introduces a "view pattern" for loop structures, enabling gradual migration without breaking existing code. Changes: - control_form.rs: Add new ID types (LoopId, ExitEdgeId, ContinueEdgeId) and structures (LoopRegion, LoopControlShape, ExitEdge, ContinueEdge) - control_form.rs: Add view methods on LoopShape: - to_region_view() - returns LoopRegion - to_control_view() - returns LoopControlShape - to_exit_edges() - returns Vec<ExitEdge> - to_continue_edges() - returns Vec<ContinueEdge> - loop_scope_shape.rs: Use views in from_existing_boxes_legacy() - loop_to_join.rs: Add debug logging with Phase 32 views All 4 minimal lowerers (skip_ws/trim/append_defs/stage1) now use view-based block ID extraction via shared from_existing_boxes_legacy(). Tests: joinir_runner_standalone_*, joinir_vm_bridge_trim_* PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
294 lines
10 KiB
Rust
294 lines
10 KiB
Rust
//! 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::LoopId;
|
||
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;
|
||
|
||
/// 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 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("<unknown>")
|
||
);
|
||
}
|
||
return None;
|
||
}
|
||
|
||
// 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-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);
|
||
|
||
eprintln!(
|
||
"[LoopToJoinLowerer] Phase 32 views: region.header={:?}, control.exits={}, exit_edges={}, continue_edges={}",
|
||
region.header,
|
||
control.exits.len(),
|
||
exit_edges.len(),
|
||
continue_edges.len()
|
||
);
|
||
}
|
||
|
||
// Step 5: パターンに応じた lowering を実行
|
||
self.lower_with_scope(scope, func_name)
|
||
}
|
||
|
||
/// Case-A ループとしてサポートされているかチェック
|
||
///
|
||
/// Phase 31 Step 3-B: 関数名フィルタ + 構造チェック
|
||
///
|
||
/// # Case-A の定義
|
||
///
|
||
/// - 単一出口(break_targets が 1 箇所以下)
|
||
/// - ループ変数または固定変数が存在
|
||
/// - ネストループなし(将来チェック追加予定)
|
||
///
|
||
/// # Arguments
|
||
///
|
||
/// - `loop_form`: LoopForm(構造判定用)
|
||
/// - `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(
|
||
&self,
|
||
loop_form: &LoopForm,
|
||
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 31 Step 3-B: 構造チェック(追加)
|
||
// 単一出口 (break が 1 箇所以下)
|
||
if loop_form.break_targets.len() > 1 {
|
||
if self.debug {
|
||
eprintln!(
|
||
"[LoopToJoinLowerer] rejected: multiple break targets ({})",
|
||
loop_form.break_targets.len()
|
||
);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// ループ変数または固定変数がある(空ループは対象外)
|
||
if scope.carriers.is_empty() && scope.pinned.is_empty() {
|
||
if self.debug {
|
||
eprintln!("[LoopToJoinLowerer] rejected: no carriers or pinned vars");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
true
|
||
}
|
||
|
||
/// LoopScopeShape から JoinModule を生成(内部メソッド)
|
||
///
|
||
/// Phase 31 Step 2: 関数名で 4 パターンにディスパッチ
|
||
/// Step 3 で汎用 Case-A lowering に統一予定
|
||
fn lower_with_scope(
|
||
&self,
|
||
scope: LoopScopeShape,
|
||
func_name: Option<&str>,
|
||
) -> Option<JoinModule> {
|
||
let name = func_name.unwrap_or("");
|
||
|
||
// Phase 31: matches! で完全一致(is_case_a_minimal_target と同じ条件)
|
||
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)
|
||
}
|
||
_ => {
|
||
// TODO: Phase 31 Step 3 で汎用 Case A 対応
|
||
if self.debug {
|
||
eprintln!(
|
||
"[LoopToJoinLowerer] no matching pattern for {:?}, returning None",
|
||
name
|
||
);
|
||
}
|
||
None
|
||
}
|
||
};
|
||
|
||
result
|
||
}
|
||
|
||
// ========================================
|
||
// Convenience methods for specific patterns
|
||
// ========================================
|
||
|
||
/// Main.skip/1 専用の lowering
|
||
///
|
||
/// Phase 31 Step 1 の検証用メソッド。
|
||
/// 既存の `skip_ws.rs` からの移行確認に使用。
|
||
pub fn lower_minimal_skip_ws_case_a(
|
||
&self,
|
||
func: &MirFunction,
|
||
loop_form: &LoopForm,
|
||
) -> Option<JoinModule> {
|
||
self.lower(func, loop_form, Some("Main.skip/1"))
|
||
}
|
||
|
||
/// FuncScannerBox.trim/1 専用の lowering
|
||
pub fn lower_minimal_trim_case_a(
|
||
&self,
|
||
func: &MirFunction,
|
||
loop_form: &LoopForm,
|
||
) -> Option<JoinModule> {
|
||
self.lower(func, loop_form, Some("FuncScannerBox.trim/1"))
|
||
}
|
||
|
||
/// FuncScannerBox.append_defs/2 専用の lowering
|
||
pub fn lower_minimal_append_defs_case_a(
|
||
&self,
|
||
func: &MirFunction,
|
||
loop_form: &LoopForm,
|
||
) -> Option<JoinModule> {
|
||
self.lower(func, loop_form, Some("FuncScannerBox.append_defs/2"))
|
||
}
|
||
|
||
/// Stage1UsingResolverBox.resolve_for_source/5 専用の lowering
|
||
pub fn lower_minimal_stage1_case_a(
|
||
&self,
|
||
func: &MirFunction,
|
||
loop_form: &LoopForm,
|
||
) -> Option<JoinModule> {
|
||
self.lower(func, loop_form, Some("Stage1UsingResolverBox.resolve_for_source/5"))
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_lowerer_creation() {
|
||
let lowerer = LoopToJoinLowerer::new();
|
||
assert!(!lowerer.debug || lowerer.debug); // Just check it compiles
|
||
}
|
||
}
|