2025-12-05 07:47:22 +09:00
|
|
|
//! 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;
|
|
|
|
|
|
2025-12-05 13:03:48 +09:00
|
|
|
/// Context passed from the host function to the Pattern 1 lowerer
|
|
|
|
|
pub struct Pattern1Context {
|
|
|
|
|
/// The loop variable ValueId from the host function (e.g., ValueId(6) for `i`)
|
|
|
|
|
pub loop_var: ValueId,
|
|
|
|
|
/// ValueId allocator function
|
|
|
|
|
pub value_allocator: Box<dyn FnMut() -> ValueId>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Pattern1Context {
|
|
|
|
|
/// Create a standalone context with hardcoded ValueIds (for backward compatibility)
|
|
|
|
|
pub fn standalone() -> Self {
|
|
|
|
|
let mut counter = 1000u32;
|
|
|
|
|
Self {
|
|
|
|
|
loop_var: ValueId(counter),
|
|
|
|
|
value_allocator: Box::new(move || {
|
|
|
|
|
counter += 1;
|
|
|
|
|
ValueId(counter)
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-05 07:47:22 +09:00
|
|
|
/// Lower Pattern 1 (Simple While Loop) to JoinIR
|
|
|
|
|
///
|
|
|
|
|
/// This is a minimal implementation for loop_min_while.hako.
|
2025-12-05 13:03:48 +09:00
|
|
|
/// It generates JoinIR that integrates with the host function's variable bindings.
|
|
|
|
|
///
|
|
|
|
|
/// # Phase 188-Impl-2: Host Variable Integration
|
|
|
|
|
///
|
|
|
|
|
/// This version accepts the host's loop variable ValueId and allocates fresh IDs
|
|
|
|
|
/// for intermediate values. This ensures the generated JoinIR connects properly
|
|
|
|
|
/// to the host function's variable bindings.
|
|
|
|
|
///
|
|
|
|
|
/// If called without a context (from legacy code), it uses standalone mode with
|
|
|
|
|
/// hardcoded ValueIds for backward compatibility.
|
2025-12-05 07:47:22 +09:00
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `_scope` - LoopScopeShape (reserved for future generic implementation)
|
2025-12-05 13:03:48 +09:00
|
|
|
/// * `ctx` - Pattern1Context containing host variable bindings (or None for standalone)
|
2025-12-05 07:47:22 +09:00
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
|
|
|
|
|
/// * `None` - Pattern not matched (fallback to other lowerers)
|
2025-12-05 13:03:48 +09:00
|
|
|
pub fn lower_simple_while_minimal(
|
|
|
|
|
_scope: LoopScopeShape,
|
|
|
|
|
ctx: Option<Pattern1Context>,
|
|
|
|
|
) -> Option<JoinModule> {
|
|
|
|
|
let mut ctx = ctx.unwrap_or_else(Pattern1Context::standalone);
|
2025-12-05 07:47:22 +09:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
// ==================================================================
|
2025-12-05 13:03:48 +09:00
|
|
|
// ValueId allocation (Phase 188-Impl-2: Use host variable + allocator)
|
2025-12-05 07:47:22 +09:00
|
|
|
// ==================================================================
|
2025-12-05 13:03:48 +09:00
|
|
|
// Host's loop variable (e.g., ValueId(6) for `i`)
|
|
|
|
|
let i_init = ctx.loop_var;
|
|
|
|
|
|
|
|
|
|
// Allocate fresh IDs for local values
|
|
|
|
|
let loop_result = (ctx.value_allocator)();
|
|
|
|
|
let const_0_main = (ctx.value_allocator)();
|
2025-12-05 07:47:22 +09:00
|
|
|
|
|
|
|
|
// loop_step locals
|
2025-12-05 13:03:48 +09:00
|
|
|
let i_param = (ctx.value_allocator)();
|
|
|
|
|
let const_3 = (ctx.value_allocator)();
|
|
|
|
|
let cmp_lt = (ctx.value_allocator)();
|
|
|
|
|
let exit_cond = (ctx.value_allocator)();
|
|
|
|
|
let const_1 = (ctx.value_allocator)();
|
|
|
|
|
let i_next = (ctx.value_allocator)();
|
2025-12-05 07:47:22 +09:00
|
|
|
|
|
|
|
|
// ==================================================================
|
|
|
|
|
// main() function
|
|
|
|
|
// ==================================================================
|
|
|
|
|
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
|
|
|
|
|
|
2025-12-05 13:03:48 +09:00
|
|
|
// Phase 188-Impl-2: Skip i_init = 0 (host already initialized the variable)
|
|
|
|
|
// The host's ValueId (i_init) is already bound to 0 in the host function
|
2025-12-05 07:47:22 +09:00
|
|
|
|
2025-12-05 13:03:48 +09:00
|
|
|
// result = loop_step(i_init) ← Use host's i directly
|
2025-12-05 07:47:22 +09:00
|
|
|
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)
|
2025-12-05 12:50:05 +09:00
|
|
|
// Phase 188-Impl-1-E: Use Print instruction
|
2025-12-05 07:47:22 +09:00
|
|
|
loop_step_func
|
|
|
|
|
.body
|
2025-12-05 12:50:05 +09:00
|
|
|
.push(JoinInst::Compute(MirLikeInst::Print {
|
|
|
|
|
value: i_param,
|
2025-12-05 07:47:22 +09:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|