Files
hakorune/src/mir/join_ir/lowering/mod.rs
2025-12-01 11:10:46 +09:00

307 lines
12 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.

//! JoinIR Lowering Functions
//!
//! Phase 27.9: Modular separation of MIR → JoinIR lowering implementations.
//!
//! このモジュールは各種 MIR 関数を JoinIR に変換する lowering 関数を提供します。
//!
//! ## 構成:
//! - `common.rs`: CFG sanity checks と lowering 共通ユーティリティPhase 27.10
//! - `value_id_ranges.rs`: ValueId 範囲管理Phase 27.13+
//! - `min_loop.rs`: JoinIrMin.main/0 専用の最小ループ lowering
//! - `skip_ws.rs`: Main.skip/1 の空白スキップ lowering手書き版MIR自動解析版
//! - `funcscanner_trim.rs`: FuncScannerBox.trim/1 の trim lowering
//! - `stage1_using_resolver.rs`: Stage1UsingResolverBox.resolve_for_source entries loop loweringPhase 27.12
//! - `funcscanner_append_defs.rs`: FuncScannerBox._append_defs/2 の配列結合 loweringPhase 27.14
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
pub mod common;
pub mod exit_args_resolver;
pub mod funcscanner_append_defs;
pub mod funcscanner_trim;
pub mod generic_case_a;
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
pub mod if_dry_runner; // Phase 33-10.0
pub mod if_merge; // Phase 33-7
pub mod if_phi_context; // Phase 61-1
pub mod if_phi_spec; // Phase 61-2
pub mod if_select; // Phase 33
pub mod loop_form_intake;
pub mod loop_scope_shape;
pub mod loop_to_join;
pub mod min_loop;
pub mod skip_ws;
pub mod stage1_using_resolver;
pub mod stageb_body;
pub mod stageb_funcscanner;
pub mod type_hint_policy; // Phase 65.5: 型ヒントポリシー箱化
pub mod type_inference; // Phase 65-2-A
pub mod value_id_ranges;
// Re-export public lowering functions
pub use funcscanner_append_defs::lower_funcscanner_append_defs_to_joinir;
pub use funcscanner_trim::lower_funcscanner_trim_to_joinir;
// Phase 31: LoopToJoinLowerer 統一箱
pub use loop_to_join::LoopToJoinLowerer;
// Phase 30 F-3: 旧 lower_case_a_loop_to_joinir_for_minimal_skip_ws は _with_scope に置き換え済みのため削除
pub use min_loop::lower_min_loop_to_joinir;
pub use skip_ws::lower_skip_ws_to_joinir;
pub use stage1_using_resolver::lower_stage1_usingresolver_to_joinir;
pub use stageb_body::lower_stageb_body_to_joinir;
pub use stageb_funcscanner::lower_stageb_funcscanner_to_joinir;
// Phase 33: If/Else → Select lowering entry point
use crate::mir::join_ir::JoinInst;
use crate::mir::{BasicBlockId, MirFunction};
/// Phase 33-9.1: Loop lowering対象関数の判定
///
/// これらの関数は Phase 32/33 で LoopToJoinLowerer によって処理されます。
/// If lowering (Select/IfMerge) の対象から除外することで、Loop/If の責務を明確に分離します。
///
/// ## 対象関数6本
/// - Main.skip/1: 空白スキップループ
/// - FuncScannerBox.trim/1: 前後空白削除ループ
/// - FuncScannerBox.append_defs/2: 配列結合ループ
/// - Stage1UsingResolverBox.resolve_for_source/5: using解析ループ
/// - StageBBodyExtractorBox.build_body_src/2: Stage-B本体抽出ループ
/// - StageBFuncScannerBox.scan_all_boxes/1: Stage-B Box走査ループ
///
/// ## 将来の拡張
/// NYASH_JOINIR_LOWER_GENERIC=1 で汎用 Case-A ループにも拡張可能
pub(crate) fn is_loop_lowered_function(name: &str) -> bool {
const LOOP_LOWERED_FUNCTIONS: &[&str] = &[
"Main.skip/1",
"FuncScannerBox.trim/1",
"FuncScannerBox.append_defs/2",
"Stage1UsingResolverBox.resolve_for_source/5",
"StageBBodyExtractorBox.build_body_src/2",
"StageBFuncScannerBox.scan_all_boxes/1",
];
LOOP_LOWERED_FUNCTIONS.contains(&name)
}
/// Phase 61-4: ループ外 If の JoinIR 対象関数判定
///
/// HAKO_JOINIR_IF_TOPLEVEL=1 有効時に、ループ外 if の JoinIR 経路を試行する関数。
/// 段階的に対象を拡大するため、最小限のホワイトリストから開始。
///
/// ## 対象関数
/// - IfSelectTest.*: テスト専用関数群
/// - IfToplevelTest.*: ループ外 if テスト専用Phase 61-4
/// - JsonShapeToMap._read_value_from_pair/1: Phase 33-4 で検証済み
///
/// ## 使用方法
/// if_form.rs から呼び出され、関数名がホワイトリストに含まれる場合のみ
/// JoinIR 経路を試行する。
pub fn is_joinir_if_toplevel_target(name: &str) -> bool {
// Test prefixes (always enabled)
if name.starts_with("IfSelectTest.")
|| name.starts_with("IfToplevelTest.")
|| name.starts_with("IfMergeTest.")
{
return true;
}
// Explicit approvals (Phase 33-4 verified)
matches!(
name,
"JsonShapeToMap._read_value_from_pair/1"
| "Stage1JsonScannerBox.value_start_after_key_pos/2"
)
}
/// Phase 33-7: Try to lower if/else to JoinIR Select/IfMerge instruction
///
/// Scope:
/// - Only applies to whitelisted functions:
/// - IfSelectTest.* (Phase 33-2/33-3)
/// - IfMergeTest.* (Phase 33-7)
/// - JsonShapeToMap._read_value_from_pair/1 (Phase 33-4 Stage-1)
/// - Stage1JsonScannerBox.value_start_after_key_pos/2 (Phase 33-4 Stage-B)
/// - Requires JoinIR If-select toggle (HAKO_JOINIR_IF_SELECT / joinir_if_select_enabled)
/// - Falls back to traditional if_phi on pattern mismatch
///
/// Pattern selection:
/// - 1 variable → Select
/// - 2+ variables → IfMerge
///
/// Phase 61-1: If-in-loop support
/// - `context` parameter: If-in-loop context (carrier_names for loop variables)
/// - None = Pure If, Some(_) = If-in-loop
///
/// Returns Some(JoinInst::Select) or Some(JoinInst::IfMerge) if pattern matched, None otherwise.
pub fn try_lower_if_to_joinir(
func: &MirFunction,
block_id: BasicBlockId,
debug: bool,
context: Option<&if_phi_context::IfPhiContext>, // Phase 61-1: If-in-loop context
) -> Option<JoinInst> {
// 1. dev/Core トグルチェック
//
// - Core: joinir_core_enabled() / joinir_if_select_enabled()
// - Dev: joinir_dev_enabled()(詳細ログ等)
//
// 実際の挙動切り替えは joinir_if_select_enabled() に集約し、
// Core/Dev ポリシーは config::env 側で判定する。
if !crate::config::env::joinir_if_select_enabled() {
return None;
}
let core_on = crate::config::env::joinir_core_enabled();
let strict_on = crate::config::env::joinir_strict_enabled();
// Phase 33-9.1: Loop専任関数の除外Loop/If責務分離
// Loop lowering対象関数はIf loweringの対象外にすることで、
// 1関数につき1 loweringの原則を保証します
if is_loop_lowered_function(&func.signature.name) {
return None;
}
// Phase 33-8: デバッグログレベル取得0-3
let debug_level = crate::config::env::joinir_debug_level();
let _debug = debug || debug_level >= 1;
// 2. Phase 33-8: 関数名ガード拡張(テスト + Stage-1 rollout + 明示承認)
let is_allowed =
// Test functions (always enabled)
func.signature.name.starts_with("IfSelectTest.") ||
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
func.signature.name.starts_with("IfMergeTest.") ||
func.signature.name.starts_with("IfToplevelTest.") || // Phase 61-4: loop-outside if test
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
// Stage-1 rollout (env-controlled)
(crate::config::env::joinir_stage1_enabled() &&
func.signature.name.starts_with("Stage1")) ||
// Explicit approvals (Phase 33-4で検証済み, always on)
matches!(func.signature.name.as_str(),
"JsonShapeToMap._read_value_from_pair/1" |
"Stage1JsonScannerBox.value_start_after_key_pos/2"
);
// Phase 80: Core ON のときは許可リストを「JoinIRをまず試す」対象とみなす。
// Core OFF のときは従来どおり whitelist + env に頼る。
if !is_allowed || !core_on {
// Core OFF かつ許可外なら従来のガードでスキップ
if !is_allowed {
if debug_level >= 2 {
eprintln!(
"[try_lower_if_to_joinir] skipping non-allowed function: {}",
func.signature.name
);
}
return None;
}
}
let strict_allowed = strict_on && core_on && is_allowed;
if debug_level >= 1 {
eprintln!(
"[try_lower_if_to_joinir] trying to lower {}",
func.signature.name
);
}
// 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン)
// IfMerge が成功すればそれを返す、失敗したら Select を試行
// Phase 61-1: context がある場合は with_context() を使用
let if_merge_lowerer = if let Some(ctx) = context {
if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone())
} else {
if_merge::IfMergeLowerer::new(debug_level)
};
if if_merge_lowerer.can_lower_to_if_merge(func, block_id) {
if let Some(result) = if_merge_lowerer.lower_if_to_if_merge(func, block_id) {
if debug_level >= 1 {
eprintln!(
"[try_lower_if_to_joinir] ✅ IfMerge lowering used for {}",
func.signature.name
);
}
return Some(result);
}
}
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
// Phase 61-1: context がある場合は with_context() を使用
let if_select_lowerer = if let Some(ctx) = context {
if_select::IfSelectLowerer::with_context(debug_level, ctx.clone())
} else {
if_select::IfSelectLowerer::new(debug_level)
};
if !if_select_lowerer.can_lower_to_select(func, block_id) {
if debug_level >= 1 {
eprintln!(
"[try_lower_if_to_joinir] pattern not matched for {}",
func.signature.name
);
}
if strict_allowed {
panic!(
"[joinir/if] strict mode: pattern not matched for {}",
func.signature.name
);
}
return None;
}
let result = if_select_lowerer.lower_if_to_select(func, block_id);
if result.is_some() && debug_level >= 1 {
eprintln!(
"[try_lower_if_to_joinir] ✅ Select lowering used for {}",
func.signature.name
);
}
if result.is_none() && strict_allowed {
panic!(
"[joinir/if] strict mode: lowering failed for {}",
func.signature.name
);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
/// Phase 33-9.1: is_loop_lowered_function() の動作確認
#[test]
fn test_is_loop_lowered_function() {
// Loop 専任関数6本は true を返す
assert!(is_loop_lowered_function("Main.skip/1"));
assert!(is_loop_lowered_function("FuncScannerBox.trim/1"));
assert!(is_loop_lowered_function("FuncScannerBox.append_defs/2"));
assert!(is_loop_lowered_function(
"Stage1UsingResolverBox.resolve_for_source/5"
));
assert!(is_loop_lowered_function(
"StageBBodyExtractorBox.build_body_src/2"
));
assert!(is_loop_lowered_function(
"StageBFuncScannerBox.scan_all_boxes/1"
));
// If lowering 対象関数は false を返す
assert!(!is_loop_lowered_function("IfSelectTest.simple_return/0"));
assert!(!is_loop_lowered_function("IfMergeTest.multiple_true/0"));
assert!(!is_loop_lowered_function(
"JsonShapeToMap._read_value_from_pair/1"
));
assert!(!is_loop_lowered_function(
"Stage1JsonScannerBox.value_start_after_key_pos/2"
));
// 一般的な関数も false を返す
assert!(!is_loop_lowered_function("SomeBox.some_method/3"));
assert!(!is_loop_lowered_function("Main.main/0"));
}
}