Files
hakorune/src/mir/join_ir/lowering/generic_case_a.rs
nyash-codex 31e458e7fa refactor(joinir): introduce CaseAContext to consolidate common lowering logic
- Add CaseAContext struct in loop_scope_shape.rs to centralize:
  - LoopForm validation
  - intake_loop_form invocation
  - LoopScopeShape construction
  - Variable name → ValueId mapping
  - pinned_ids/carrier_ids/exit_args resolution

- Refactor all 4 generic_case_a.rs functions to use CaseAContext:
  - lower_case_a_loop_to_joinir_for_minimal_skip_ws
  - lower_case_a_loop_to_joinir_for_trim_minimal
  - lower_case_a_loop_to_joinir_for_append_defs_minimal
  - lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal

- Remove unused name_to_header_id field from LoopFormIntake
  (was duplicate of header_snapshot)

Code reduction: ~200 lines of duplicated pattern → 4 lines

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 06:32:08 +09:00

1051 lines
33 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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<JoinModule> {
// 追加の 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<String, ValueId> = 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<ValueId> = ctx
.ordered_pinned
.iter()
.chain(ctx.ordered_carriers.iter())
.map(|name| entry_name_to_id.get(name).copied())
.collect::<Option<_>>()?;
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<JoinModule> {
// 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<String, ValueId> = 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<ValueId> = ctx
.ordered_pinned
.iter()
.chain(ctx.ordered_carriers.iter())
.map(|name| entry_name_to_id.get(name).copied())
.collect::<Option<_>>()?;
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 を返す。
/// - 既存の手書き JoinIRappend_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<JoinModule> {
// 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<String, ValueId> = 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<ValueId> = ctx
.ordered_pinned
.iter()
.chain(ctx.ordered_carriers.iter())
.map(|name| entry_name_to_id.get(name).copied())
.collect::<Option<_>>()?;
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<JoinModule> {
// 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<String, ValueId> = 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<ValueId> = ctx
.ordered_pinned
.iter()
.chain(ctx.ordered_carriers.iter())
.map(|name| entry_name_to_id.get(name).copied())
.collect::<Option<_>>()?;
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)
}