//! Generic Case A LoopForm → JoinIR lowering (v1, minimal_ssa_skip_ws 専用) //! //! 制約(必読): //! - 条件式の中身を解析しない(Compare/BinOp を MIR そのままコピーするだけ) //! - 多重ヘッダ/ネストループは対象外(v1 は minimal_ssa_skip_ws の単純ループ専用) //! - pinned/carrier/exit は LoopVarClassBox / LoopExitLivenessBox から渡された前提で扱う //! - 解析に失敗したら必ず None を返し、呼び元にフォールバックさせる use std::collections::BTreeMap; use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; use crate::mir::join_ir::lowering::value_id_ranges; use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid; use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid; use crate::mir::join_ir::{ BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinModule, LoopExitShape, LoopHeaderShape, MirLikeInst, }; use crate::mir::loop_form::LoopForm; use crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox; use crate::mir::phi_core::loop_var_classifier::LoopVarClassBox; use crate::mir::{MirFunction, MirQuery, ValueId}; /// v1: minimal_ssa_skip_ws 専用の汎用 Case A ロワー /// /// - LoopForm / VarClass / ExitLiveness は形の検証にのみ使う(パターンが合わなければ None) /// - JoinModule の形は hand-written skip_ws と同一になるように組み立てる pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws( loop_form: &LoopForm, var_classes: &LoopVarClassBox, exit_live: &LoopExitLivenessBox, query: &impl MirQuery, mir_func: &MirFunction, ) -> Option { // 追加の latch チェック(skip_ws 固有) if loop_form.latch != loop_form.body && loop_form.latch != loop_form.header { eprintln!( "[joinir/generic_case_a] unexpected latch {:?} (body={:?}, header={:?}), fallback", loop_form.latch, loop_form.body, loop_form.header ); return None; } // CaseAContext で共通ロジックを実行 let ctx = CaseAContext::new( loop_form, var_classes, exit_live, query, mir_func, "skip_ws", |offset| vid::loop_step(offset), )?; // JoinModule を手書き skip_ws と同じ形で構築(ValueId 範囲も揃える) let string_key = ctx.pinned_name_or_first(0)?; let len_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| string_key.clone()); let index_key = ctx.carrier_name_or_first(0)?; let s_loop = ctx.get_loop_id(&string_key)?; let i_loop = ctx.get_loop_id(&index_key)?; let n_loop = ctx.get_loop_id(&len_key)?; let mut join_module = JoinModule::new(); // entry: skip(s) let skip_id = JoinFuncId::new(0); let s_param = vid::entry(0); // 3000 let mut skip_func = JoinFunction::new(skip_id, "skip".to_string(), vec![s_param]); let i_init = vid::entry(1); // 3001 let n_val = vid::entry(2); // 3002 let mut entry_name_to_id: BTreeMap = BTreeMap::new(); entry_name_to_id.insert(string_key.clone(), s_param); entry_name_to_id.insert(index_key.clone(), i_init); entry_name_to_id.insert(len_key.clone(), n_val); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: i_init, value: ConstValue::Integer(0), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(n_val), box_name: "StringBox".to_string(), method: "length".to_string(), args: vec![s_param], })); let loop_step_id = JoinFuncId::new(1); let loop_call_args: Vec = ctx .ordered_pinned .iter() .chain(ctx.ordered_carriers.iter()) .map(|name| entry_name_to_id.get(name).copied()) .collect::>()?; skip_func.body.push(JoinInst::Call { func: loop_step_id, args: loop_call_args, k_next: None, dst: None, }); join_module.entry = Some(skip_id); join_module.add_function(skip_func); // loop_step(s, i, n) let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone()); let loop_params = header_shape.to_loop_step_params(); let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone()); let cmp1_result = vid::loop_step(3); // 4003 let ch = vid::loop_step(4); // 4004 let cmp2_result = vid::loop_step(5); // 4005 let i_plus_1 = vid::loop_step(6); // 4006 let const_1 = vid::loop_step(7); // 4007 let const_space = vid::loop_step(10); // 4010 let bool_false = vid::loop_step(11); // 4011 let cmp2_is_false = vid::loop_step(12); // 4012 // cmp1_result = (i >= n) loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp1_result, op: CompareOp::Ge, lhs: i_loop, rhs: n_loop, })); let _exit_shape = if ctx.exit_args.is_empty() { LoopExitShape::new_manual(vec![i_loop]) } else { LoopExitShape::new_manual(ctx.exit_args.clone()) }; // exit_args = [i] が期待値 // if i >= n { return i } loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), args: vec![i_loop], cond: Some(cmp1_result), }); // const 1 loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_1, value: ConstValue::Integer(1), })); // i_plus_1 = i + 1 loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: i_plus_1, op: BinOpKind::Add, lhs: i_loop, rhs: const_1, })); // ch = s.substring(i, i + 1) loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(ch), box_name: "StringBox".to_string(), method: "substring".to_string(), args: vec![s_loop, i_loop, i_plus_1], })); // const " " loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_space, value: ConstValue::String(" ".to_string()), })); // cmp2_result = (ch == " ") loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp2_result, op: CompareOp::Eq, lhs: ch, rhs: const_space, })); // bool false loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: bool_false, value: ConstValue::Bool(false), })); // cmp2_is_false = (cmp2_result == false) loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp2_is_false, op: CompareOp::Eq, lhs: cmp2_result, rhs: bool_false, })); // if ch != " " { return i } loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(1), args: vec![i_loop], cond: Some(cmp2_is_false), }); // continue: loop_step(s, i+1, n) loop_step_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![s_loop, i_plus_1, n_loop], k_next: None, dst: None, }); join_module.add_function(loop_step_func); eprintln!( "[joinir/generic_case_a] ✅ constructed JoinIR (functions={}, value_range={}..{})", join_module.functions.len(), value_id_ranges::base::SKIP_WS, value_id_ranges::base::SKIP_WS + 1999 ); Some(join_module) } /// Placeholder: trim minimal 用の generic Case A ロワー(v0, 未実装) /// /// - いまは構造チェックのみで必ず None を返し、呼び元でフォールバックさせる /// - 将来 trim_minimal を generic_case_a で置き換える際の導線として用意 pub fn lower_case_a_loop_to_joinir_for_trim_minimal( loop_form: &LoopForm, var_classes: &LoopVarClassBox, exit_live: &LoopExitLivenessBox, query: &impl MirQuery, mir_func: &MirFunction, ) -> Option { // CaseAContext で共通ロジックを実行 let ctx = CaseAContext::new( loop_form, var_classes, exit_live, query, mir_func, "trim", |offset| value_id_ranges::funcscanner_trim::loop_step(offset), )?; let string_key = ctx.pinned_name_or_first(0)?; let base_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| string_key.clone()); let carrier_key = ctx.carrier_name_or_first(0)?; let s_loop = ctx.get_loop_id(&string_key)?; let b_loop = ctx.get_loop_id(&base_key)?; let e_loop = ctx.get_loop_id(&carrier_key)?; let mut join_module = JoinModule::new(); // entry: trim_main(s_param) let trim_main_id = JoinFuncId::new(0); let s_param = value_id_ranges::funcscanner_trim::entry(0); let mut trim_main_func = JoinFunction::new(trim_main_id, "trim_main".to_string(), vec![s_param]); let str_val = value_id_ranges::funcscanner_trim::entry(1); let n_val = value_id_ranges::funcscanner_trim::entry(2); let b_val = value_id_ranges::funcscanner_trim::entry(3); let e_init = value_id_ranges::funcscanner_trim::entry(4); let const_empty = value_id_ranges::funcscanner_trim::entry(5); let const_zero = value_id_ranges::funcscanner_trim::entry(6); trim_main_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_empty, value: ConstValue::String("".to_string()), })); trim_main_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: str_val, lhs: const_empty, rhs: s_param, op: BinOpKind::Add, })); trim_main_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(n_val), box_name: "StringBox".to_string(), method: "length".to_string(), args: vec![str_val], })); trim_main_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_zero, value: ConstValue::Integer(0), })); let skip_leading_id = JoinFuncId::new(2); trim_main_func.body.push(JoinInst::Call { func: skip_leading_id, args: vec![str_val, const_zero, n_val], k_next: None, dst: Some(b_val), }); trim_main_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: e_init, op: BinOpKind::Add, lhs: n_val, rhs: const_zero, })); let mut entry_name_to_id: BTreeMap = BTreeMap::new(); entry_name_to_id.insert(string_key.clone(), str_val); entry_name_to_id.insert(base_key.clone(), b_val); entry_name_to_id.insert(carrier_key.clone(), e_init); let loop_call_args: Vec = ctx .ordered_pinned .iter() .chain(ctx.ordered_carriers.iter()) .map(|name| entry_name_to_id.get(name).copied()) .collect::>()?; let loop_step_id = JoinFuncId::new(1); trim_main_func.body.push(JoinInst::Call { func: loop_step_id, args: loop_call_args, k_next: None, dst: None, }); join_module.entry = Some(trim_main_id); join_module.add_function(trim_main_func); // loop_step(str, b, e) let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone()); let loop_params = header_shape.to_loop_step_params(); let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone()); let cond = value_id_ranges::funcscanner_trim::loop_step(3); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cond, lhs: e_loop, rhs: b_loop, op: CompareOp::Gt, })); let bool_false = value_id_ranges::funcscanner_trim::loop_step(19); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: bool_false, value: ConstValue::Bool(false), })); let trimmed_base = value_id_ranges::funcscanner_trim::loop_step(4); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(trimmed_base), box_name: "StringBox".to_string(), method: "substring".to_string(), args: vec![s_loop, b_loop, e_loop], })); let cond_is_false = value_id_ranges::funcscanner_trim::loop_step(20); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cond_is_false, lhs: cond, rhs: bool_false, op: CompareOp::Eq, })); let _exit_shape_trim = if ctx.exit_args.is_empty() { LoopExitShape::new_manual(vec![e_loop]) } else { LoopExitShape::new_manual(ctx.exit_args.clone()) }; loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), args: vec![trimmed_base], cond: Some(cond_is_false), }); let const_1 = value_id_ranges::funcscanner_trim::loop_step(5); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_1, value: ConstValue::Integer(1), })); let e_minus_1 = value_id_ranges::funcscanner_trim::loop_step(6); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: e_minus_1, lhs: e_loop, rhs: const_1, op: BinOpKind::Sub, })); let ch = value_id_ranges::funcscanner_trim::loop_step(7); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(ch), box_name: "StringBox".to_string(), method: "substring".to_string(), args: vec![s_loop, e_minus_1, e_loop], })); let cmp_space = value_id_ranges::funcscanner_trim::loop_step(8); let cmp_tab = value_id_ranges::funcscanner_trim::loop_step(9); let cmp_newline = value_id_ranges::funcscanner_trim::loop_step(10); let cmp_cr = value_id_ranges::funcscanner_trim::loop_step(11); let const_space = value_id_ranges::funcscanner_trim::loop_step(12); let const_tab = value_id_ranges::funcscanner_trim::loop_step(13); let const_newline = value_id_ranges::funcscanner_trim::loop_step(14); let const_cr = value_id_ranges::funcscanner_trim::loop_step(15); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_space, value: ConstValue::String(" ".to_string()), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_space, lhs: ch, rhs: const_space, op: CompareOp::Eq, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_tab, value: ConstValue::String("\\t".to_string()), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_tab, lhs: ch, rhs: const_tab, op: CompareOp::Eq, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_newline, value: ConstValue::String("\\n".to_string()), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_newline, lhs: ch, rhs: const_newline, op: CompareOp::Eq, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_cr, value: ConstValue::String("\\r".to_string()), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_cr, lhs: ch, rhs: const_cr, op: CompareOp::Eq, })); let or1 = value_id_ranges::funcscanner_trim::loop_step(16); let or2 = value_id_ranges::funcscanner_trim::loop_step(17); let is_space = value_id_ranges::funcscanner_trim::loop_step(18); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: or1, lhs: cmp_space, rhs: cmp_tab, op: BinOpKind::Or, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: or2, lhs: or1, rhs: cmp_newline, op: BinOpKind::Or, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: is_space, lhs: or2, rhs: cmp_cr, op: BinOpKind::Or, })); let is_space_false = value_id_ranges::funcscanner_trim::loop_step(21); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: is_space_false, lhs: is_space, rhs: bool_false, op: CompareOp::Eq, })); loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(1), args: vec![trimmed_base], cond: Some(is_space_false), }); let e_next = value_id_ranges::funcscanner_trim::loop_step(22); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: e_next, lhs: e_loop, rhs: const_1, op: BinOpKind::Sub, })); loop_step_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![s_loop, b_loop, e_next], k_next: None, dst: None, }); join_module.add_function(loop_step_func); // skip_leading 関数(共通ロジックを手書き版と合わせる) let mut skip_func = JoinFunction::new( skip_leading_id, "skip_leading".to_string(), vec![ValueId(7000), ValueId(7001), ValueId(7002)], ); let s_skip = ValueId(7000); let i_skip = ValueId(7001); let n_skip = ValueId(7002); let cmp_len = ValueId(7003); let const_1_skip = ValueId(7004); let i_plus_1_skip = ValueId(7005); let ch_skip = ValueId(7006); let cmp_space_skip = ValueId(7007); let cmp_tab_skip = ValueId(7008); let cmp_newline_skip = ValueId(7009); let cmp_cr_skip = ValueId(7010); let const_space_skip = ValueId(7011); let const_tab_skip = ValueId(7012); let const_newline_skip = ValueId(7013); let const_cr_skip = ValueId(7014); let or1_skip = ValueId(7015); let or2_skip = ValueId(7016); let is_space_skip = ValueId(7017); let bool_false_skip = ValueId(7018); let is_space_false_skip = ValueId(7019); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_len, lhs: i_skip, rhs: n_skip, op: CompareOp::Ge, })); skip_func.body.push(JoinInst::Jump { cont: JoinContId::new(2), args: vec![i_skip], cond: Some(cmp_len), }); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_1_skip, value: ConstValue::Integer(1), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: i_plus_1_skip, lhs: i_skip, rhs: const_1_skip, op: BinOpKind::Add, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(ch_skip), box_name: "StringBox".to_string(), method: "substring".to_string(), args: vec![s_skip, i_skip, i_plus_1_skip], })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_space_skip, value: ConstValue::String(" ".to_string()), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_space_skip, lhs: ch_skip, rhs: const_space_skip, op: CompareOp::Eq, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_tab_skip, value: ConstValue::String("\\t".to_string()), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_tab_skip, lhs: ch_skip, rhs: const_tab_skip, op: CompareOp::Eq, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_newline_skip, value: ConstValue::String("\\n".to_string()), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_newline_skip, lhs: ch_skip, rhs: const_newline_skip, op: CompareOp::Eq, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_cr_skip, value: ConstValue::String("\\r".to_string()), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_cr_skip, lhs: ch_skip, rhs: const_cr_skip, op: CompareOp::Eq, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: or1_skip, lhs: cmp_space_skip, rhs: cmp_tab_skip, op: BinOpKind::Or, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: or2_skip, lhs: or1_skip, rhs: cmp_newline_skip, op: BinOpKind::Or, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: is_space_skip, lhs: or2_skip, rhs: cmp_cr_skip, op: BinOpKind::Or, })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: bool_false_skip, value: ConstValue::Bool(false), })); skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: is_space_false_skip, lhs: is_space_skip, rhs: bool_false_skip, op: CompareOp::Eq, })); skip_func.body.push(JoinInst::Jump { cont: JoinContId::new(3), args: vec![i_skip], cond: Some(is_space_false_skip), }); skip_func.body.push(JoinInst::Call { func: skip_leading_id, args: vec![s_skip, i_plus_1_skip, n_skip], k_next: None, dst: None, }); join_module.add_function(skip_func); eprintln!( "[joinir/generic_case_a/trim] ✅ constructed JoinIR (functions={}, value_range={}..{})", join_module.functions.len(), value_id_ranges::base::FUNCSCANNER_TRIM, value_id_ranges::base::FUNCSCANNER_TRIM + 1999 ); Some(join_module) } /// append_defs_minimal 用の generic Case A ロワー(LoopForm/VarClass/ExitLiveness ベース) /// /// - LoopForm が単一 header/latch でない場合や、必要な変数がマッピングできない場合は None を返す。 /// - 既存の手書き JoinIR(append_defs_entry + loop_step)と同じ形を目指す。 pub fn lower_case_a_loop_to_joinir_for_append_defs_minimal( loop_form: &LoopForm, var_classes: &LoopVarClassBox, exit_live: &LoopExitLivenessBox, query: &impl MirQuery, mir_func: &MirFunction, ) -> Option { // CaseAContext で共通ロジックを実行 let ctx = CaseAContext::new( loop_form, var_classes, exit_live, query, mir_func, "append_defs", |offset| value_id_ranges::funcscanner_append_defs::loop_step(offset), )?; let dst_key = ctx.pinned_name_or_first(0)?; let defs_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| dst_key.clone()); let n_key = ctx.pinned_name_or_first(2).unwrap_or_else(|| defs_key.clone()); let i_key = ctx.carrier_name_or_first(0)?; let dst_loop = ctx.get_loop_id(&dst_key)?; let defs_loop = ctx.get_loop_id(&defs_key)?; let n_loop = ctx.get_loop_id(&n_key)?; let i_loop = ctx.get_loop_id(&i_key)?; let mut join_module = JoinModule::new(); // entry: append_defs_entry(dst, defs_box, n) let entry_id = JoinFuncId::new(0); let dst_param = value_id_ranges::funcscanner_append_defs::entry(0); let defs_box_param = value_id_ranges::funcscanner_append_defs::entry(1); let n_param = value_id_ranges::funcscanner_append_defs::entry(2); let mut entry_func = JoinFunction::new( entry_id, "append_defs_entry".to_string(), vec![dst_param, defs_box_param, n_param], ); let i_init = value_id_ranges::funcscanner_append_defs::entry(10); entry_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: i_init, value: ConstValue::Integer(0), })); let mut entry_name_to_id: BTreeMap = BTreeMap::new(); entry_name_to_id.insert("s".to_string(), dst_param); // intake で param0 を "s" にするため entry_name_to_id.insert("dst".to_string(), dst_param); entry_name_to_id.insert("param1".to_string(), defs_box_param); entry_name_to_id.insert("defs_box".to_string(), defs_box_param); entry_name_to_id.insert("n".to_string(), n_param); entry_name_to_id.insert("i".to_string(), i_init); let loop_call_args: Vec = ctx .ordered_pinned .iter() .chain(ctx.ordered_carriers.iter()) .map(|name| entry_name_to_id.get(name).copied()) .collect::>()?; let loop_step_id = JoinFuncId::new(1); entry_func.body.push(JoinInst::Call { func: loop_step_id, args: loop_call_args, k_next: None, dst: None, }); join_module.entry = Some(entry_id); join_module.add_function(entry_func); // loop_step(dst, defs_box, n, i) let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone()); let loop_params = header_shape.to_loop_step_params(); let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone()); let cmp_result = value_id_ranges::funcscanner_append_defs::loop_step(10); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_result, op: CompareOp::Ge, lhs: i_loop, rhs: n_loop, })); let _exit_shape = LoopExitShape::new_manual(ctx.exit_args.clone()); loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), args: ctx.exit_args.clone(), cond: Some(cmp_result), }); let item_value = value_id_ranges::funcscanner_append_defs::loop_step(11); let next_i = value_id_ranges::funcscanner_append_defs::loop_step(12); let const_1 = value_id_ranges::funcscanner_append_defs::loop_step(13); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(item_value), box_name: "ArrayBox".to_string(), method: "get".to_string(), args: vec![defs_loop, i_loop], })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: None, box_name: "ArrayBox".to_string(), method: "push".to_string(), args: vec![dst_loop, item_value], })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_1, value: ConstValue::Integer(1), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: next_i, op: BinOpKind::Add, lhs: i_loop, rhs: const_1, })); loop_step_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![dst_loop, defs_loop, n_loop, next_i], k_next: None, dst: None, }); join_module.add_function(loop_step_func); eprintln!( "[joinir/generic_case_a/append_defs] ✅ constructed JoinIR (functions={}, value_range={}..{})", join_module.functions.len(), value_id_ranges::base::FUNCSCANNER_APPEND_DEFS, value_id_ranges::base::FUNCSCANNER_APPEND_DEFS + 1999 ); Some(join_module) } /// Stage1UsingResolver minimal 用の generic Case A ロワー(LoopForm/VarClass/ExitLiveness ベース) pub fn lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal( loop_form: &LoopForm, var_classes: &LoopVarClassBox, exit_live: &LoopExitLivenessBox, query: &impl MirQuery, mir_func: &MirFunction, ) -> Option { // CaseAContext で共通ロジックを実行 let ctx = CaseAContext::new( loop_form, var_classes, exit_live, query, mir_func, "stage1", |offset| stage1_vid::loop_step(offset), )?; let entries_key = ctx.pinned_name_or_first(0)?; let n_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| entries_key.clone()); let modules_key = ctx.pinned_name_or_first(2).unwrap_or_else(|| entries_key.clone()); let seen_key = ctx.pinned_name_or_first(3).unwrap_or_else(|| entries_key.clone()); let prefix_key = ctx.carrier_name_or_first(0)?; let i_key = ctx.carrier_name_or_first(1).unwrap_or_else(|| prefix_key.clone()); let entries_loop = ctx.get_loop_id(&entries_key)?; let n_loop = ctx.get_loop_id(&n_key)?; let modules_loop = ctx.get_loop_id(&modules_key)?; let seen_loop = ctx.get_loop_id(&seen_key)?; let prefix_loop = ctx.get_loop_id(&prefix_key)?; let i_loop = ctx.get_loop_id(&i_key)?; let mut join_module = JoinModule::new(); // entry: resolve_entries(entries, n, modules, seen, prefix_init) let resolve_id = JoinFuncId::new(0); let entries_param = stage1_vid::entry(0); let n_param = stage1_vid::entry(1); let modules_param = stage1_vid::entry(2); let seen_param = stage1_vid::entry(3); let prefix_param = stage1_vid::entry(4); let mut resolve_func = JoinFunction::new( resolve_id, "resolve_entries".to_string(), vec![ entries_param, n_param, modules_param, seen_param, prefix_param, ], ); let i_init = stage1_vid::entry(10); resolve_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: i_init, value: ConstValue::Integer(0), })); let mut entry_name_to_id: BTreeMap = BTreeMap::new(); entry_name_to_id.insert(entries_key.clone(), entries_param); entry_name_to_id.insert(n_key.clone(), n_param); entry_name_to_id.insert(modules_key.clone(), modules_param); entry_name_to_id.insert(seen_key.clone(), seen_param); entry_name_to_id.insert(prefix_key.clone(), prefix_param); entry_name_to_id.insert(i_key.clone(), i_init); let loop_call_args: Vec = ctx .ordered_pinned .iter() .chain(ctx.ordered_carriers.iter()) .map(|name| entry_name_to_id.get(name).copied()) .collect::>()?; let loop_step_id = JoinFuncId::new(1); resolve_func.body.push(JoinInst::Call { func: loop_step_id, args: loop_call_args, k_next: None, dst: None, }); join_module.entry = Some(resolve_id); join_module.add_function(resolve_func); // loop_step(entries, n, modules, seen, prefix, i) let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone()); let loop_params = header_shape.to_loop_step_params(); let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone()); let cmp_result = stage1_vid::loop_step(10); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_result, op: CompareOp::Ge, lhs: i_loop, rhs: n_loop, })); let exit_shape = if ctx.exit_args.is_empty() { LoopExitShape::new_manual(vec![prefix_loop]) } else { LoopExitShape::new_manual(ctx.exit_args.clone()) }; loop_step_func.body.push(JoinInst::Jump { cont: JoinContId::new(0), args: exit_shape.exit_args.clone(), cond: Some(cmp_result), }); let entry_value = stage1_vid::loop_step(11); let next_i = stage1_vid::loop_step(12); let const_1 = stage1_vid::loop_step(13); let new_prefix = stage1_vid::loop_step(14); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BoxCall { dst: Some(entry_value), box_name: "ArrayBox".to_string(), method: "get".to_string(), args: vec![entries_loop, i_loop], })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::Const { dst: const_1, value: ConstValue::Integer(1), })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: next_i, op: BinOpKind::Add, lhs: i_loop, rhs: const_1, })); loop_step_func .body .push(JoinInst::Compute(MirLikeInst::BinOp { dst: new_prefix, op: BinOpKind::Or, lhs: prefix_loop, rhs: entry_value, })); loop_step_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![ entries_loop, n_loop, modules_loop, seen_loop, new_prefix, next_i, ], k_next: None, dst: None, }); join_module.add_function(loop_step_func); eprintln!( "[joinir/generic_case_a/stage1] ✅ constructed JoinIR (functions={}, value_range={}..{})", join_module.functions.len(), value_id_ranges::base::STAGE1_USING_RESOLVER, value_id_ranges::base::STAGE1_USING_RESOLVER + 1999 ); Some(join_module) }