Files
hakorune/src/mir/join_ir/lowering/nested_loop_minimal.rs
tomoaki d0527bcc2a feat(joinir): Phase 188.3-P3.3 - Pattern6 continuation generation + Call callee fix
Phase 3-3 完了: 4関数モデル JoinIR 生成
- nested_loop_minimal.rs (337行, 新規): 4関数モデル実装
  - main(): エントリーポイント
  - loop_step(i, sum): outer loop header
  - inner_step(j, i_outer, sum): inner loop (tail recursion)
  - k_inner_exit(i, sum): outer continuation after inner loop
  - k_exit(sum): 最終 exit
- pattern6_nested_minimal.rs: lowering pipeline 実装
  - boundary 構築 (continuation_func_ids 設定)
  - JoinIRConversionPipeline 呼び出し
- instruction_rewriter.rs: latch incoming 拡張
  - continuation→header 呼び出し対応

Call callee 修正:
- call_generator.rs: callee フィールドを Callee::Global に設定
- joinir_block_converter.rs: emit_call_pair 使用に統一

smoke test 追加:
- phase1883_nested_minimal_vm.sh (integration)

既知の問題 (次タスク):
- ValueId(104) undefined: PHI/merge 問題
- JoinIR 関数パラメータの MIR マッピングが不完全

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 06:51:43 +09:00

398 lines
13 KiB
Rust

//! Phase 188.3 P1: Pattern 6 (Nested Loop Minimal) JoinIR Lowerer
//!
//! Target: apps/tests/phase1883_nested_minimal.hako
//!
//! Code:
//! ```nyash
//! static box Main {
//! main() {
//! local sum = 0
//! local i = 0
//! loop(i < 3) {
//! local j = 0
//! loop(j < 3) {
//! sum = sum + 1
//! j = j + 1
//! }
//! i = i + 1
//! }
//! return sum
//! }
//! }
//! ```
//!
//! Expected JoinIR (4 functions):
//! ```text
//! fn main(i0, sum0):
//! Call(loop_step, [i0, sum0])
//! Ret 0
//!
//! fn loop_step(i, sum): // outer loop
//! exit_cond = !(i < 3)
//! Jump(k_exit, [sum], cond=exit_cond)
//! j0 = 0
//! Call(inner_step, [j0, i, sum])
//!
//! fn inner_step(j, i_outer, sum): // inner loop (tail recursion)
//! exit_cond = !(j < 3)
//! Jump(k_inner_exit, [i_outer, sum], cond=exit_cond)
//! sum_next = sum + 1
//! j_next = j + 1
//! Call(inner_step, [j_next, i_outer, sum_next])
//!
//! fn k_inner_exit(i, sum): // outer continuation (after inner loop)
//! i_next = i + 1
//! Call(loop_step, [i_next, sum])
//!
//! fn k_exit(sum):
//! Ret sum
//! ```
//!
//! ## Design Notes (Phase 188.3)
//!
//! - **4-function model**: main, loop_step (outer), inner_step, k_inner_exit, k_exit
//! - **Carrier design**: sum is passed as argument (not global)
//! - **Merge control**: continuation_func_ids includes k_exit, k_inner_exit, inner_step
//! - **Pattern1-based**: Both outer and inner are Pattern1 (no break/continue)
use crate::mir::join_ir::lowering::canonical_names as cn;
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,
};
/// Canonical names for Pattern 6 continuations
pub const INNER_STEP: &str = "inner_step";
pub const K_INNER_EXIT: &str = "k_inner_exit";
/// Lower Pattern 6 (Nested Loop Minimal) to JoinIR
///
/// # Phase 188.3 P1: Minimal nested loop implementation
///
/// This version generates JoinIR using **JoinValueSpace** for unified ValueId allocation.
/// It uses the Local region (1000+) to avoid collision with Param region (100-999).
///
/// ## 4-Function Model
///
/// - **main(i0, sum0)**: Entry point, calls loop_step
/// - **loop_step(i, sum)**: Outer loop header, initializes & calls inner loop
/// - **inner_step(j, i_outer, sum)**: Inner loop body (tail recursion)
/// - **k_inner_exit(i, sum)**: Outer continuation after inner loop exits
/// - **k_exit(sum)**: Final exit continuation
///
/// # 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 slots**: main() params (i0, sum0) for outer loop variables
/// - **Caller responsibility**: Create JoinInlineBoundary to map param ValueIds to host variables
pub(crate) fn lower_nested_loop_minimal(
_scope: LoopScopeShape,
join_value_space: &mut JoinValueSpace,
) -> Option<JoinModule> {
let mut join_module = JoinModule::new();
// ==================================================================
// Function IDs allocation
// ==================================================================
let main_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
let inner_step_id = JoinFuncId::new(2);
let k_inner_exit_id = JoinFuncId::new(3);
let k_exit_id = JoinFuncId::new(4);
// ==================================================================
// ValueId allocation - main() function
// ==================================================================
let i_main_param = join_value_space.alloc_param(); // i0 (outer loop var)
let sum_main_param = join_value_space.alloc_param(); // sum0 (carrier)
let loop_result = join_value_space.alloc_local(); // result from loop_step
let const_0_main = join_value_space.alloc_local(); // return value
// ==================================================================
// ValueId allocation - loop_step (outer loop)
// ==================================================================
let i_step_param = join_value_space.alloc_param(); // outer loop var
let sum_step_param = join_value_space.alloc_param(); // sum carrier
let const_3 = join_value_space.alloc_local(); // outer limit (3)
let cmp_i_lt = join_value_space.alloc_local(); // i < 3
let exit_cond_outer = join_value_space.alloc_local(); // !(i < 3)
let j0 = join_value_space.alloc_local(); // inner loop init (0)
// ==================================================================
// ValueId allocation - inner_step (inner loop)
// ==================================================================
let j_inner_param = join_value_space.alloc_param(); // inner loop var
let i_inner_param = join_value_space.alloc_param(); // outer var (read-only)
let sum_inner_param = join_value_space.alloc_param(); // sum carrier
let const_3_inner = join_value_space.alloc_local(); // inner limit (3)
let cmp_j_lt = join_value_space.alloc_local(); // j < 3
let exit_cond_inner = join_value_space.alloc_local(); // !(j < 3)
let const_1_sum = join_value_space.alloc_local(); // increment (1)
let sum_next = join_value_space.alloc_local(); // sum + 1
let const_1_j = join_value_space.alloc_local(); // increment (1)
let j_next = join_value_space.alloc_local(); // j + 1
// ==================================================================
// ValueId allocation - k_inner_exit (outer continuation)
// ==================================================================
let i_kexit_param = join_value_space.alloc_param(); // outer var
let sum_kexit_param = join_value_space.alloc_param(); // sum carrier
let const_1_i = join_value_space.alloc_local(); // increment (1)
let i_next = join_value_space.alloc_local(); // i + 1
// ==================================================================
// ValueId allocation - k_exit (final exit)
// ==================================================================
let sum_exit_param = join_value_space.alloc_param(); // sum for return
// ==================================================================
// main() function
// ==================================================================
let mut main_func =
JoinFunction::new(main_id, cn::MAIN.to_string(), vec![i_main_param, sum_main_param]);
// result = loop_step(i_main_param, sum_main_param)
main_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_main_param, sum_main_param],
k_next: None,
dst: Some(loop_result),
});
// return 0 (statement position)
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, sum) - outer loop header
// ==================================================================
let mut loop_step_func = JoinFunction::new(
loop_step_id,
cn::LOOP_STEP.to_string(),
vec![i_step_param, sum_step_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_i_lt = (i < 3)
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_i_lt,
op: CompareOp::Lt,
lhs: i_step_param,
rhs: const_3,
}));
// Step 3: exit_cond_outer = !cmp_i_lt
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::UnaryOp {
dst: exit_cond_outer,
op: UnaryOp::Not,
operand: cmp_i_lt,
}));
// Jump(k_exit, [sum], cond=exit_cond_outer)
loop_step_func.body.push(JoinInst::Jump {
cont: k_exit_id.as_cont(),
args: vec![sum_step_param],
cond: Some(exit_cond_outer),
});
// j0 = 0 (inner loop initialization)
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: j0,
value: ConstValue::Integer(0),
}));
// Call(inner_step, [j0, i, sum]) - no continuation (tail call)
loop_step_func.body.push(JoinInst::Call {
func: inner_step_id,
args: vec![j0, i_step_param, sum_step_param],
k_next: None,
dst: None,
});
join_module.add_function(loop_step_func);
// ==================================================================
// inner_step(j, i_outer, sum) - inner loop (tail recursion)
// ==================================================================
let mut inner_step_func = JoinFunction::new(
inner_step_id,
INNER_STEP.to_string(),
vec![j_inner_param, i_inner_param, sum_inner_param],
);
// exit_cond = !(j < 3)
// Step 1: const 3
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_3_inner,
value: ConstValue::Integer(3),
}));
// Step 2: cmp_j_lt = (j < 3)
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_j_lt,
op: CompareOp::Lt,
lhs: j_inner_param,
rhs: const_3_inner,
}));
// Step 3: exit_cond_inner = !cmp_j_lt
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::UnaryOp {
dst: exit_cond_inner,
op: UnaryOp::Not,
operand: cmp_j_lt,
}));
// Jump(k_inner_exit, [i_outer, sum], cond=exit_cond_inner)
inner_step_func.body.push(JoinInst::Jump {
cont: k_inner_exit_id.as_cont(),
args: vec![i_inner_param, sum_inner_param],
cond: Some(exit_cond_inner),
});
// sum_next = sum + 1
// Step 1: const 1
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1_sum,
value: ConstValue::Integer(1),
}));
// Step 2: sum_next = sum + 1
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: sum_next,
op: BinOpKind::Add,
lhs: sum_inner_param,
rhs: const_1_sum,
}));
// j_next = j + 1
// Step 1: const 1
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1_j,
value: ConstValue::Integer(1),
}));
// Step 2: j_next = j + 1
inner_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: j_next,
op: BinOpKind::Add,
lhs: j_inner_param,
rhs: const_1_j,
}));
// Call(inner_step, [j_next, i_outer, sum_next]) - tail recursion
inner_step_func.body.push(JoinInst::Call {
func: inner_step_id,
args: vec![j_next, i_inner_param, sum_next],
k_next: None,
dst: None,
});
join_module.add_function(inner_step_func);
// ==================================================================
// k_inner_exit(i, sum) - outer continuation (after inner loop)
// ==================================================================
let mut k_inner_exit_func = JoinFunction::new(
k_inner_exit_id,
K_INNER_EXIT.to_string(),
vec![i_kexit_param, sum_kexit_param],
);
// i_next = i + 1
// Step 1: const 1
k_inner_exit_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1_i,
value: ConstValue::Integer(1),
}));
// Step 2: i_next = i + 1
k_inner_exit_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: i_next,
op: BinOpKind::Add,
lhs: i_kexit_param,
rhs: const_1_i,
}));
// Call(loop_step, [i_next, sum]) - tail call to outer loop
k_inner_exit_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_next, sum_kexit_param],
k_next: None,
dst: None,
});
join_module.add_function(k_inner_exit_func);
// ==================================================================
// k_exit(sum) - final exit continuation
// ==================================================================
let mut k_exit_func = JoinFunction::new(k_exit_id, cn::K_EXIT.to_string(), vec![sum_exit_param]);
// return sum
k_exit_func.body.push(JoinInst::Ret {
value: Some(sum_exit_param),
});
join_module.add_function(k_exit_func);
// Set entry point
join_module.entry = Some(main_id);
eprintln!("[joinir/pattern6] Generated JoinIR for Nested Loop Minimal Pattern");
eprintln!("[joinir/pattern6] Functions: main, loop_step, inner_step, k_inner_exit, k_exit");
Some(join_module)
}