diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 6e18f888..780c9cd8 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -6,12 +6,14 @@ //! //! ## 構成: //! - `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 lowering(Phase 27.12) pub mod common; +pub mod value_id_ranges; pub mod funcscanner_trim; pub mod min_loop; pub mod skip_ws; diff --git a/src/mir/join_ir/lowering/stage1_using_resolver.rs b/src/mir/join_ir/lowering/stage1_using_resolver.rs index a3913243..f803773a 100644 --- a/src/mir/join_ir/lowering/stage1_using_resolver.rs +++ b/src/mir/join_ir/lowering/stage1_using_resolver.rs @@ -42,7 +42,8 @@ //! } //! ``` -use crate::mir::join_ir::lowering::common::{dispatch_lowering, ensure_entry_has_succs, log_fallback}; +use crate::mir::join_ir::lowering::common::{dispatch_lowering, ensure_entry_has_succs, has_const_int, log_fallback}; +use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as vid; use crate::mir::join_ir::{JoinModule}; use crate::mir::query::MirQueryBox; @@ -78,19 +79,159 @@ pub fn lower_stage1_usingresolver_to_joinir(module: &crate::mir::MirModule) -> O /// - ArrayBox.get(i) → 文字列連結 のシンプルな形に固定 /// /// 将来的には MIR から実際の処理を抽出して精密化する。 -fn build_stage1_using_resolver_joinir(_module: &crate::mir::MirModule) -> Option { - eprintln!("[joinir/stage1_using_resolver/build] Phase 27.12 minimal implementation"); - eprintln!("[joinir/stage1_using_resolver/build] Generating simplified JoinIR for entries loop"); +fn build_stage1_using_resolver_joinir(module: &crate::mir::MirModule) -> Option { + use crate::mir::join_ir::*; - // Phase 27.12 MVP: 最小実装 - // TODO: 実際の JoinIR 構築を実装 - // - // 構造: - // - Function 0: resolve_entries(entries, n, modules, seen, prefix_init) - // - Function 1: loop_step(entries, n, modules, seen, prefix, i) + // Phase 27.13: ターゲット関数が存在するかチェック + let _target_func = module.functions.get("Stage1UsingResolverBox.resolve_for_source/1")?; - eprintln!("[joinir/stage1_using_resolver/build] TODO: JoinIR construction not yet implemented"); - None + eprintln!("[joinir/stage1_using_resolver/build] Phase 27.13 implementation"); + eprintln!("[joinir/stage1_using_resolver/build] Generating JoinIR for entries loop"); + eprintln!("[joinir/stage1_using_resolver/build] Using ValueId range: 7000-8999 (via value_id_ranges)"); + + // Step 1: JoinModule を構築 + let mut join_module = JoinModule::new(); + + // resolve_entries 関数(entry): + // fn resolve_entries(entries, n, modules, seen, prefix_init) -> String { + // let i_init = 0; + // loop_step(entries, n, modules, seen, prefix_init, i_init) + // } + let resolve_id = JoinFuncId::new(0); + let entries_param = vid::entry(0); // 7000 + let n_param = vid::entry(1); // 7001 + let modules_param = vid::entry(2); // 7002 + let seen_param = vid::entry(3); // 7003 + let prefix_init_param = vid::entry(4); // 7004 + + let mut resolve_func = JoinFunction::new( + resolve_id, + "resolve_entries".to_string(), + vec![entries_param, n_param, modules_param, seen_param, prefix_init_param], + ); + + let i_init = vid::entry(10); // 7010 + + // i_init = 0 + resolve_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: i_init, + value: ConstValue::Integer(0), + })); + + // loop_step(entries, n, modules, seen, prefix_init, i_init) + let loop_step_id = JoinFuncId::new(1); + resolve_func.body.push(JoinInst::Call { + func: loop_step_id, + args: vec![entries_param, n_param, modules_param, seen_param, prefix_init_param, i_init], + k_next: None, + dst: None, + }); + + join_module.entry = Some(resolve_id); + join_module.add_function(resolve_func); + + // Phase 27.13: loop_step の Pinned/Carrier 構造を明示 + // UsingResolver entries ループの場合: + // - Pinned: entries (ArrayBox), n (Integer), modules (MapBox), seen (MapBox) + // - Carrier: prefix (String), i (Integer) + // - Exit: prefix (String) + let entries_loop = vid::loop_step(0); // 8000 - Pinned + let n_loop = vid::loop_step(1); // 8001 - Pinned + let modules_loop = vid::loop_step(2); // 8002 - Pinned + let seen_loop = vid::loop_step(3); // 8003 - Pinned + let prefix_loop = vid::loop_step(4); // 8004 - Carrier + let i_loop = vid::loop_step(5); // 8005 - Carrier + + let _header_shape = LoopHeaderShape::new_manual( + vec![entries_loop, n_loop, modules_loop, seen_loop], // Pinned + vec![prefix_loop, i_loop], // Carrier + ); + + // loop_step 関数: + // fn loop_step(entries, n, modules, seen, prefix, i) -> String { + // if i >= n { return prefix } + // let entry = entries.get(i) + // let next_i = i + 1 + // // 簡略化: 文字列連結のみ(should_emit, path 解決等は省略) + // let new_prefix = prefix + entry // 実際は "\n" + code + "\n" + // loop_step(entries, n, modules, seen, new_prefix, next_i) + // } + let mut loop_step_func = JoinFunction::new( + loop_step_id, + "loop_step".to_string(), + vec![entries_loop, n_loop, modules_loop, seen_loop, prefix_loop, i_loop], + ); + + let cmp_result = vid::loop_step(10); // 8010 + let entry_value = vid::loop_step(11); // 8011 + let next_i = vid::loop_step(12); // 8012 + let const_1 = vid::loop_step(13); // 8013 + let new_prefix = vid::loop_step(14); // 8014 + + // cmp_result = (i >= n) + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { + dst: cmp_result, + op: CompareOp::Ge, + lhs: i_loop, + rhs: n_loop, + })); + + // Phase 27.13: Exit φ の意味を LoopExitShape で明示 + // UsingResolver entries ループ脱出時は prefix の値を返す(最終的な連結文字列) + let _exit_shape = LoopExitShape::new_manual(vec![prefix_loop]); // exit_args = [prefix] + + // if i >= n { return prefix } + loop_step_func.body.push(JoinInst::Jump { + cont: JoinContId::new(0), + args: vec![prefix_loop], // ← LoopExitShape.exit_args に対応 + cond: Some(cmp_result), + }); + + // entry = entries.get(i) + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall { + dst: Some(entry_value), + box_name: "ArrayBox".to_string(), + method: "get".to_string(), + args: vec![entries_loop, i_loop], + })); + + // const_1 = 1 + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_1, + value: ConstValue::Integer(1), + })); + + // next_i = i + 1 + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: next_i, + op: BinOpKind::Add, + lhs: i_loop, + rhs: const_1, + })); + + // 簡略化: 文字列連結のみ(実際の should_emit, path 解決等は省略) + // new_prefix = prefix + entry (実際は "\n" + code + "\n" だが、ここでは簡略化) + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: new_prefix, + op: BinOpKind::Or, // String concatenation uses Or in JoinIR + lhs: prefix_loop, + rhs: entry_value, + })); + + // loop_step(entries, n, modules, seen, new_prefix, next_i) - tail recursion + loop_step_func.body.push(JoinInst::Call { + func: loop_step_id, + args: vec![entries_loop, n_loop, modules_loop, seen_loop, new_prefix, next_i], + k_next: None, + dst: None, + }); + + join_module.add_function(loop_step_func); + + eprintln!("[joinir/stage1_using_resolver/build] ✅ JoinIR construction completed"); + eprintln!("[joinir/stage1_using_resolver/build] Functions: {}", join_module.functions.len()); + + Some(join_module) } /// Phase 27.12: MIR-based lowering for Stage1UsingResolverBox.resolve_for_source @@ -128,11 +269,21 @@ fn lower_from_mir(module: &crate::mir::MirModule) -> Option { } // CFG Check 2: Entry block contains expected patterns - // TODO: Implement pattern detection - // - has_const_int(&query, entry, 0) - // - has_array_method(&query, entry, "length") - // - has_array_method(&query, ..., "get") - // - has_binop(&query, ..., BinaryOp::Add) + // Pattern 1: i = 0 (初期化) + if !has_const_int(&query, entry, 0) { + log_fallback("stage1_using_resolver", "Const(0) not found in entry block"); + return lower_handwritten(module); + } + + // Pattern 2: entries.length() の検出 + // Phase 27.13: 簡略化のため、複雑な BoxCall 検出は省略 + // 将来的には has_array_method(&query, entry, "length") を実装可能 + // 現時点では Const(0) の存在で最小限の sanity check とする + + // TODO (Phase 27.14+): より厳密な CFG パターンマッチング + // - has_array_method(&query, entry_or_succ, "length") でループ上限 n 確認 + // - has_array_method(&query, loop_body, "get") でループ内配列アクセス確認 + // - has_binop(&query, loop_body, BinaryOp::Add) で i + 1 確認 eprintln!("[joinir/stage1_using_resolver/mir] CFG sanity checks passed ✅"); diff --git a/src/mir/join_ir/lowering/value_id_ranges.rs b/src/mir/join_ir/lowering/value_id_ranges.rs new file mode 100644 index 00000000..8fc18a31 --- /dev/null +++ b/src/mir/join_ir/lowering/value_id_ranges.rs @@ -0,0 +1,158 @@ +//! ValueId Range Allocation for JoinIR Lowering Modules +//! +//! This module manages ValueId ranges for each JoinIR lowering module to prevent +//! ID conflicts when multiple lowerings coexist. +//! +//! ## Current Allocations +//! +//! | Module | Range | Entry | Loop | Notes | +//! |-----------------------|-----------|-------|-------|-------| +//! | min_loop | 1000-2999 | 1000+ | 2000+ | Minimal loop test | +//! | skip_ws | 3000-4999 | 3000+ | 4000+ | Skip whitespace | +//! | funcscanner_trim | 5000-6999 | 5000+ | 6000+ | Trim whitespace | +//! | stage1_using_resolver | 7000-8999 | 7000+ | 8000+ | Stage-1 using resolver | +//! +//! ## Usage Example +//! +//! ```rust,ignore +//! use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as vid; +//! +//! let entries_param = vid::entry(0); // ValueId(7000) +//! let n_param = vid::entry(1); // ValueId(7001) +//! let entries_loop = vid::loop_step(0); // ValueId(8000) +//! let n_loop = vid::loop_step(1); // ValueId(8001) +//! ``` +//! +//! ## Future Extensions +//! +//! When adding new lowering modules, allocate ranges in increments of 2000: +//! - 9000-10999 (next available) +//! - 11000-12999 +//! - 13000-14999 +//! - etc. + +use crate::mir::ValueId; + +/// Base addresses for each lowering module's ValueId range +pub mod base { + /// min_loop: Minimal loop test (1000-2999) + pub const MIN_LOOP: u32 = 1000; + + /// skip_ws: Skip whitespace loop (3000-4999) + pub const SKIP_WS: u32 = 3000; + + /// funcscanner_trim: Trim whitespace loop (5000-6999) + pub const FUNCSCANNER_TRIM: u32 = 5000; + + /// stage1_using_resolver: Stage-1 using resolver entries loop (7000-8999) + pub const STAGE1_USING_RESOLVER: u32 = 7000; +} + +/// Helper function to create ValueId from base + offset +/// +/// This is a const fn, so it's computed at compile time with zero runtime cost. +#[inline] +pub const fn id(base: u32, offset: u32) -> ValueId { + ValueId(base + offset) +} + +/// ValueId helpers for min_loop lowering module +pub mod min_loop { + use super::{base, id}; + use crate::mir::ValueId; + + /// Entry function ValueIds (1000-1999) + #[inline] + pub const fn entry(offset: u32) -> ValueId { + id(base::MIN_LOOP, offset) + } + + /// Loop function ValueIds (2000-2999) + #[inline] + pub const fn loop_step(offset: u32) -> ValueId { + id(base::MIN_LOOP, 1000 + offset) + } +} + +/// ValueId helpers for skip_ws lowering module +pub mod skip_ws { + use super::{base, id}; + use crate::mir::ValueId; + + /// Entry function ValueIds (3000-3999) + #[inline] + pub const fn entry(offset: u32) -> ValueId { + id(base::SKIP_WS, offset) + } + + /// Loop function ValueIds (4000-4999) + #[inline] + pub const fn loop_step(offset: u32) -> ValueId { + id(base::SKIP_WS, 1000 + offset) + } +} + +/// ValueId helpers for funcscanner_trim lowering module +pub mod funcscanner_trim { + use super::{base, id}; + use crate::mir::ValueId; + + /// Entry function ValueIds (5000-5999) + #[inline] + pub const fn entry(offset: u32) -> ValueId { + id(base::FUNCSCANNER_TRIM, offset) + } + + /// Loop function ValueIds (6000-6999) + #[inline] + pub const fn loop_step(offset: u32) -> ValueId { + id(base::FUNCSCANNER_TRIM, 1000 + offset) + } +} + +/// ValueId helpers for stage1_using_resolver lowering module +pub mod stage1_using_resolver { + use super::{base, id}; + use crate::mir::ValueId; + + /// Entry function ValueIds (7000-7999) + #[inline] + pub const fn entry(offset: u32) -> ValueId { + id(base::STAGE1_USING_RESOLVER, offset) + } + + /// Loop function ValueIds (8000-8999) + #[inline] + pub const fn loop_step(offset: u32) -> ValueId { + id(base::STAGE1_USING_RESOLVER, 1000 + offset) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_value_id_ranges_no_overlap() { + // min_loop: 1000-2999 + assert_eq!(min_loop::entry(0).0, 1000); + assert_eq!(min_loop::loop_step(999).0, 2999); + + // skip_ws: 3000-4999 + assert_eq!(skip_ws::entry(0).0, 3000); + assert_eq!(skip_ws::loop_step(999).0, 4999); + + // funcscanner_trim: 5000-6999 + assert_eq!(funcscanner_trim::entry(0).0, 5000); + assert_eq!(funcscanner_trim::loop_step(999).0, 6999); + + // stage1_using_resolver: 7000-8999 + assert_eq!(stage1_using_resolver::entry(0).0, 7000); + assert_eq!(stage1_using_resolver::loop_step(999).0, 8999); + + // Verify no overlaps + assert!(min_loop::loop_step(999).0 < skip_ws::entry(0).0); + assert!(skip_ws::loop_step(999).0 < funcscanner_trim::entry(0).0); + assert!(funcscanner_trim::loop_step(999).0 < stage1_using_resolver::entry(0).0); + } +}