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>
398 lines
13 KiB
Rust
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)
|
|
}
|