//! Phase 188-Impl-1: Pattern 1 (Simple While Loop) Minimal Lowerer //! //! Target: apps/tests/loop_min_while.hako //! //! Code: //! ```nyash //! static box Main { //! main() { //! local i = 0 //! loop(i < 3) { //! print(i) //! i = i + 1 //! } //! return 0 //! } //! } //! ``` //! //! Expected JoinIR: //! ```text //! fn main(): //! i_init = 0 //! result = loop_step(i_init) //! return 0 //! //! fn loop_step(i): //! exit_cond = !(i < 3) //! Jump(k_exit, [], cond=exit_cond) // early return if i >= 3 //! print(i) // body //! i_next = i + 1 // increment //! Call(loop_step, [i_next]) // tail recursion //! //! fn k_exit(): //! return 0 //! ``` //! //! ## Design Notes //! //! This is a MINIMAL implementation targeting loop_min_while.hako specifically. //! It establishes the infrastructure for Pattern 1 lowering, which will be //! generalized in future phases. //! //! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later. use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::{ BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst, UnaryOp, }; use crate::mir::ValueId; /// Lower Pattern 1 (Simple While Loop) to JoinIR /// /// This is a minimal implementation for loop_min_while.hako. /// It generates hardcoded JoinIR for the specific pattern. /// /// # Arguments /// /// * `_scope` - LoopScopeShape (reserved for future generic implementation) /// /// # Returns /// /// * `Some(JoinModule)` - Successfully lowered to JoinIR /// * `None` - Pattern not matched (fallback to other lowerers) pub fn lower_simple_while_minimal(_scope: LoopScopeShape) -> Option { // Phase 188-Impl-1: Hardcoded JoinIR for loop_min_while.hako // This establishes the infrastructure. Generic implementation in Phase 188-Impl-2+. let mut join_module = JoinModule::new(); // ================================================================== // Function IDs allocation // ================================================================== let main_id = JoinFuncId::new(0); let loop_step_id = JoinFuncId::new(1); let k_exit_id = JoinFuncId::new(2); // ================================================================== // ValueId allocation (hardcoded for minimal implementation) // ================================================================== let i_init = ValueId(1000); let loop_result = ValueId(1001); let const_0_main = ValueId(1002); // loop_step locals let i_param = ValueId(2000); let const_3 = ValueId(2001); let cmp_lt = ValueId(2002); let exit_cond = ValueId(2003); let const_1 = ValueId(2004); let i_next = ValueId(2005); // ================================================================== // main() function // ================================================================== let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]); // i_init = 0 main_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: i_init, value: ConstValue::Integer(0), })); // result = loop_step(i_init) main_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![i_init], k_next: None, dst: Some(loop_result), }); // return 0 main_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_0_main, value: ConstValue::Integer(0), })); main_func.body.push(JoinInst::Ret { value: Some(const_0_main), }); join_module.add_function(main_func); // ================================================================== // loop_step(i) function // ================================================================== let mut loop_step_func = JoinFunction::new( loop_step_id, "loop_step".to_string(), vec![i_param], ); // exit_cond = !(i < 3) // Step 1: const 3 loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_3, value: ConstValue::Integer(3), })); // Step 2: cmp_lt = (i < 3) loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_lt, op: CompareOp::Lt, lhs: i_param, rhs: const_3, })); // Step 3: exit_cond = !cmp_lt loop_step_func .body .push(JoinInst::Compute(MirLikeInst::UnaryOp { dst: exit_cond, op: UnaryOp::Not, operand: cmp_lt, })); // Jump(k_exit, [], cond=exit_cond) loop_step_func.body.push(JoinInst::Jump { cont: k_exit_id.as_cont(), args: vec![], cond: Some(exit_cond), }); // print(i) // Phase 188-Impl-1: Use BoxCall for print (no ExternCall variant in MirLikeInst) // Note: print is a built-in extern function, but we represent it as a BoxCall here loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: None, box_name: "print".to_string(), method: "call".to_string(), // External function as method call args: vec![i_param], })); // i_next = i + 1 // Step 1: const 1 loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_1, value: ConstValue::Integer(1), })); // Step 2: i_next = i + 1 loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: i_next, op: BinOpKind::Add, lhs: i_param, rhs: const_1, })); // Call(loop_step, [i_next]) // tail recursion loop_step_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![i_next], k_next: None, // CRITICAL: None for tail call dst: None, }); join_module.add_function(loop_step_func); // ================================================================== // k_exit() function // ================================================================== let mut k_exit_func = JoinFunction::new(k_exit_id, "k_exit".to_string(), vec![]); // return 0 (Pattern 1 has no exit values) let const_0_exit = ValueId(3000); k_exit_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_0_exit, value: ConstValue::Integer(0), })); k_exit_func.body.push(JoinInst::Ret { value: Some(const_0_exit), }); join_module.add_function(k_exit_func); // Set entry point join_module.entry = Some(main_id); eprintln!("[joinir/pattern1] Generated JoinIR for Simple While Pattern"); eprintln!("[joinir/pattern1] Functions: main, loop_step, k_exit"); Some(join_module) }