Files
hakorune/src/mir/join_ir/lowering/simple_while_minimal.rs
nyash-codex 6e778948db feat(joinir): Phase 202-A Pattern 1 uses JoinValueSpace
Migrated Pattern 1 (Simple While) to use JoinValueSpace for unified
ValueId allocation, following the same pattern as Pattern 2 (Phase 201).

Changes:
- simple_while_minimal.rs: Added join_value_space parameter, replaced
  value_counter with join_value_space.alloc_local()
- pattern1_minimal.rs: Create JoinValueSpace before calling lowerer
- loop_view_builder.rs: Create JoinValueSpace in try_pattern1()

Pattern 1 uses Local region (1000+) only, since it doesn't need
ConditionEnv (no Param region allocation required).

Tested:
- cargo build --release --lib: Success (0 errors, 4 warnings)
- cargo test --release --lib pattern: 119 passed
- E2E test apps/tests/loop_min_while.hako: Outputs "0 1 2" correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-09 19:08:42 +09:00

258 lines
8.1 KiB
Rust

//! 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::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp,
};
/// Lower Pattern 1 (Simple While Loop) to JoinIR
///
/// # Phase 188-Impl-3: Pure JoinIR Fragment Generation
/// # Phase 202-A: JoinValueSpace Integration
///
/// This version generates JoinIR using **JoinValueSpace** for unified ValueId allocation.
/// It uses the Local region (1000+) to avoid collision with Param region (100-999).
///
/// ## Design Philosophy
///
/// - **Box A**: JoinIR Frontend (doesn't know about host ValueIds)
/// - **Box B**: This function - converts to JoinIR with local IDs
/// - **Box C**: JoinInlineBoundary - stores boundary info
/// - **Box D**: merge_joinir_mir_blocks - injects Copy instructions
///
/// This clean separation ensures JoinIR lowerers are:
/// - Pure transformers (no side effects)
/// - Reusable (same lowerer works in any context)
/// - Testable (can test JoinIR independently)
///
/// # Arguments
///
/// * `_scope` - LoopScopeShape (reserved for future generic implementation)
/// * `join_value_space` - Unified ValueId allocator (Phase 202-A)
///
/// # Returns
///
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
/// * `None` - Pattern not matched (fallback to other lowerers)
///
/// # Boundary Contract
///
/// This function returns a JoinModule with:
/// - **Input slot**: ValueId(0) in loop_step function represents the loop variable
/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueId(0) to host's loop var
pub(crate) fn lower_simple_while_minimal(
_scope: LoopScopeShape,
join_value_space: &mut JoinValueSpace,
) -> Option<JoinModule> {
// Phase 202-A: Use JoinValueSpace for Local region allocation (1000+)
// This ensures no collision with Param region (100-999) used by ConditionEnv/CarrierInfo
let mut alloc_value = || join_value_space.alloc_local();
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 (Phase 188-Impl-3: Sequential local IDs)
// ==================================================================
// main() locals
let i_init = alloc_value(); // ValueId(0) - loop init value
let loop_result = alloc_value(); // ValueId(1) - result from loop_step
let const_0_main = alloc_value(); // ValueId(2) - return value
// loop_step locals
let i_param = alloc_value(); // ValueId(3) - parameter
let const_3 = alloc_value(); // ValueId(4) - comparison constant
let cmp_lt = alloc_value(); // ValueId(5) - i < 3
let exit_cond = alloc_value(); // ValueId(6) - !(i < 3)
let const_1 = alloc_value(); // ValueId(7) - increment constant
let i_next = alloc_value(); // ValueId(8) - i + 1
// k_exit locals
let const_0_exit = alloc_value(); // ValueId(9) - exit return value
// ==================================================================
// main() function
// ==================================================================
// Phase 188-Impl-3: main() takes i as a parameter (boundary input)
// The host will inject a Copy instruction: i_init_local = Copy host_i
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![i_init]);
// 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-E: Use Print instruction
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Print {
value: 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)
// Phase 188-Impl-3: Use pre-allocated const_0_exit (ValueId(9))
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)
}