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>
This commit is contained in:
@ -6,10 +6,9 @@
|
|||||||
//! - pinned/carrier/exit は LoopVarClassBox / LoopExitLivenessBox から渡された前提で扱う
|
//! - pinned/carrier/exit は LoopVarClassBox / LoopExitLivenessBox から渡された前提で扱う
|
||||||
//! - 解析に失敗したら必ず None を返し、呼び元にフォールバックさせる
|
//! - 解析に失敗したら必ず None を返し、呼び元にフォールバックさせる
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::exit_args_resolver::resolve_exit_args;
|
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
||||||
use crate::mir::join_ir::lowering::loop_form_intake::{intake_loop_form, LoopFormIntake};
|
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges;
|
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::skip_ws as vid;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid;
|
use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid;
|
||||||
@ -33,13 +32,8 @@ pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws(
|
|||||||
query: &impl MirQuery,
|
query: &impl MirQuery,
|
||||||
mir_func: &MirFunction,
|
mir_func: &MirFunction,
|
||||||
) -> Option<JoinModule> {
|
) -> Option<JoinModule> {
|
||||||
// 1) LoopForm が Case A っぽいか最低限チェック(header→body/exit、latch→header)
|
// 追加の latch チェック(skip_ws 固有)
|
||||||
if loop_form.header == loop_form.exit {
|
|
||||||
eprintln!("[joinir/generic_case_a] loop_form malformed (header == exit), fallback");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if loop_form.latch != loop_form.body && loop_form.latch != loop_form.header {
|
if loop_form.latch != loop_form.body && loop_form.latch != loop_form.header {
|
||||||
// minimal_skip_ws では latch==body or header 想定。違えばフォールバック。
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/generic_case_a] unexpected latch {:?} (body={:?}, header={:?}), fallback",
|
"[joinir/generic_case_a] unexpected latch {:?} (body={:?}, header={:?}), fallback",
|
||||||
loop_form.latch, loop_form.body, loop_form.header
|
loop_form.latch, loop_form.body, loop_form.header
|
||||||
@ -47,53 +41,25 @@ pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) MIR から pinned/carrier/exit 情報を抽出する
|
// CaseAContext で共通ロジックを実行
|
||||||
let LoopFormIntake {
|
let ctx = CaseAContext::new(
|
||||||
pinned_ordered: ordered_pinned,
|
loop_form,
|
||||||
carrier_ordered: ordered_carriers,
|
var_classes,
|
||||||
name_to_header_id: _name_to_id,
|
exit_live,
|
||||||
header_snapshot: header_vals_mir,
|
query,
|
||||||
exit_snapshots,
|
mir_func,
|
||||||
exit_preds: _exit_preds,
|
"skip_ws",
|
||||||
} = intake_loop_form(loop_form, var_classes, query, mir_func)?;
|
|offset| vid::loop_step(offset),
|
||||||
|
)?;
|
||||||
|
|
||||||
// JoinIR 用の ValueId を範囲から割り当て(名前→ID マップ)
|
// JoinModule を手書き skip_ws と同じ形で構築(ValueId 範囲も揃える)
|
||||||
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
|
let string_key = ctx.pinned_name_or_first(0)?;
|
||||||
let mut offset = 0;
|
let len_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| string_key.clone());
|
||||||
for name in &ordered_pinned {
|
let index_key = ctx.carrier_name_or_first(0)?;
|
||||||
name_to_loop_id.insert(name.clone(), vid::loop_step(offset));
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
for name in &ordered_carriers {
|
|
||||||
name_to_loop_id.insert(name.clone(), vid::loop_step(offset));
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinned_ids: Vec<ValueId> = ordered_pinned
|
let s_loop = ctx.get_loop_id(&string_key)?;
|
||||||
.iter()
|
let i_loop = ctx.get_loop_id(&index_key)?;
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
let n_loop = ctx.get_loop_id(&len_key)?;
|
||||||
.collect();
|
|
||||||
let carrier_ids: Vec<ValueId> = ordered_carriers
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Exit live 集合(LoopExitLivenessBox を利用)
|
|
||||||
let exit_live_set: BTreeSet<String> =
|
|
||||||
exit_live.compute_live_at_exit(query, loop_form.exit, &header_vals_mir, &exit_snapshots);
|
|
||||||
let exit_args = resolve_exit_args(&exit_live_set, &name_to_loop_id, &ordered_carriers)?;
|
|
||||||
|
|
||||||
// 3) JoinModule を手書き skip_ws と同じ形で構築(ValueId 範囲も揃える)
|
|
||||||
let string_key = ordered_pinned.get(0).cloned()?;
|
|
||||||
let len_key = ordered_pinned
|
|
||||||
.get(1)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| string_key.clone());
|
|
||||||
let index_key = ordered_carriers.get(0).cloned()?;
|
|
||||||
|
|
||||||
let s_loop = *name_to_loop_id.get(&string_key)?;
|
|
||||||
let i_loop = *name_to_loop_id.get(&index_key)?;
|
|
||||||
let n_loop = *name_to_loop_id.get(&len_key)?;
|
|
||||||
|
|
||||||
let mut join_module = JoinModule::new();
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
@ -123,9 +89,10 @@ pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let loop_step_id = JoinFuncId::new(1);
|
let loop_step_id = JoinFuncId::new(1);
|
||||||
let loop_call_args: Vec<ValueId> = ordered_pinned
|
let loop_call_args: Vec<ValueId> = ctx
|
||||||
|
.ordered_pinned
|
||||||
.iter()
|
.iter()
|
||||||
.chain(ordered_carriers.iter())
|
.chain(ctx.ordered_carriers.iter())
|
||||||
.map(|name| entry_name_to_id.get(name).copied())
|
.map(|name| entry_name_to_id.get(name).copied())
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
@ -140,7 +107,7 @@ pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws(
|
|||||||
join_module.add_function(skip_func);
|
join_module.add_function(skip_func);
|
||||||
|
|
||||||
// loop_step(s, i, n)
|
// loop_step(s, i, n)
|
||||||
let header_shape = LoopHeaderShape::new_manual(pinned_ids.clone(), carrier_ids.clone());
|
let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone());
|
||||||
let loop_params = header_shape.to_loop_step_params();
|
let loop_params = header_shape.to_loop_step_params();
|
||||||
let mut loop_step_func =
|
let mut loop_step_func =
|
||||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
||||||
@ -164,10 +131,10 @@ pub fn lower_case_a_loop_to_joinir_for_minimal_skip_ws(
|
|||||||
rhs: n_loop,
|
rhs: n_loop,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _exit_shape = if exit_args.is_empty() {
|
let _exit_shape = if ctx.exit_args.is_empty() {
|
||||||
LoopExitShape::new_manual(vec![i_loop])
|
LoopExitShape::new_manual(vec![i_loop])
|
||||||
} else {
|
} else {
|
||||||
LoopExitShape::new_manual(exit_args.clone())
|
LoopExitShape::new_manual(ctx.exit_args.clone())
|
||||||
}; // exit_args = [i] が期待値
|
}; // exit_args = [i] が期待値
|
||||||
|
|
||||||
// if i >= n { return i }
|
// if i >= n { return i }
|
||||||
@ -279,60 +246,24 @@ pub fn lower_case_a_loop_to_joinir_for_trim_minimal(
|
|||||||
query: &impl MirQuery,
|
query: &impl MirQuery,
|
||||||
mir_func: &MirFunction,
|
mir_func: &MirFunction,
|
||||||
) -> Option<JoinModule> {
|
) -> Option<JoinModule> {
|
||||||
if loop_form.header == loop_form.exit {
|
// CaseAContext で共通ロジックを実行
|
||||||
eprintln!("[joinir/generic_case_a/trim] loop_form malformed (header == exit), fallback");
|
let ctx = CaseAContext::new(
|
||||||
return None;
|
loop_form,
|
||||||
}
|
var_classes,
|
||||||
|
exit_live,
|
||||||
|
query,
|
||||||
|
mir_func,
|
||||||
|
"trim",
|
||||||
|
|offset| value_id_ranges::funcscanner_trim::loop_step(offset),
|
||||||
|
)?;
|
||||||
|
|
||||||
let LoopFormIntake {
|
let string_key = ctx.pinned_name_or_first(0)?;
|
||||||
pinned_ordered: ordered_pinned,
|
let base_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| string_key.clone());
|
||||||
carrier_ordered: ordered_carriers,
|
let carrier_key = ctx.carrier_name_or_first(0)?;
|
||||||
name_to_header_id: _name_to_id,
|
|
||||||
header_snapshot,
|
|
||||||
exit_snapshots,
|
|
||||||
exit_preds: _exit_preds,
|
|
||||||
} = intake_loop_form(loop_form, var_classes, query, mir_func)?;
|
|
||||||
|
|
||||||
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
|
let s_loop = ctx.get_loop_id(&string_key)?;
|
||||||
let mut offset = 0;
|
let b_loop = ctx.get_loop_id(&base_key)?;
|
||||||
for name in &ordered_pinned {
|
let e_loop = ctx.get_loop_id(&carrier_key)?;
|
||||||
name_to_loop_id.insert(
|
|
||||||
name.clone(),
|
|
||||||
value_id_ranges::funcscanner_trim::loop_step(offset),
|
|
||||||
);
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
for name in &ordered_carriers {
|
|
||||||
name_to_loop_id.insert(
|
|
||||||
name.clone(),
|
|
||||||
value_id_ranges::funcscanner_trim::loop_step(offset),
|
|
||||||
);
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinned_ids: Vec<ValueId> = ordered_pinned
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
let carrier_ids: Vec<ValueId> = ordered_carriers
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let exit_live_set: BTreeSet<String> =
|
|
||||||
exit_live.compute_live_at_exit(query, loop_form.exit, &header_snapshot, &exit_snapshots);
|
|
||||||
let exit_args = resolve_exit_args(&exit_live_set, &name_to_loop_id, &ordered_carriers)?;
|
|
||||||
|
|
||||||
let string_key = ordered_pinned.get(0).cloned()?;
|
|
||||||
let base_key = ordered_pinned
|
|
||||||
.get(1)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| string_key.clone());
|
|
||||||
let carrier_key = ordered_carriers.get(0).cloned()?;
|
|
||||||
|
|
||||||
let s_loop = *name_to_loop_id.get(&string_key)?;
|
|
||||||
let b_loop = *name_to_loop_id.get(&base_key)?;
|
|
||||||
let e_loop = *name_to_loop_id.get(&carrier_key)?;
|
|
||||||
|
|
||||||
let mut join_module = JoinModule::new();
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
@ -400,9 +331,10 @@ pub fn lower_case_a_loop_to_joinir_for_trim_minimal(
|
|||||||
entry_name_to_id.insert(base_key.clone(), b_val);
|
entry_name_to_id.insert(base_key.clone(), b_val);
|
||||||
entry_name_to_id.insert(carrier_key.clone(), e_init);
|
entry_name_to_id.insert(carrier_key.clone(), e_init);
|
||||||
|
|
||||||
let loop_call_args: Vec<ValueId> = ordered_pinned
|
let loop_call_args: Vec<ValueId> = ctx
|
||||||
|
.ordered_pinned
|
||||||
.iter()
|
.iter()
|
||||||
.chain(ordered_carriers.iter())
|
.chain(ctx.ordered_carriers.iter())
|
||||||
.map(|name| entry_name_to_id.get(name).copied())
|
.map(|name| entry_name_to_id.get(name).copied())
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
@ -418,7 +350,7 @@ pub fn lower_case_a_loop_to_joinir_for_trim_minimal(
|
|||||||
join_module.add_function(trim_main_func);
|
join_module.add_function(trim_main_func);
|
||||||
|
|
||||||
// loop_step(str, b, e)
|
// loop_step(str, b, e)
|
||||||
let header_shape = LoopHeaderShape::new_manual(pinned_ids.clone(), carrier_ids.clone());
|
let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone());
|
||||||
let loop_params = header_shape.to_loop_step_params();
|
let loop_params = header_shape.to_loop_step_params();
|
||||||
let mut loop_step_func =
|
let mut loop_step_func =
|
||||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
||||||
@ -461,10 +393,10 @@ pub fn lower_case_a_loop_to_joinir_for_trim_minimal(
|
|||||||
op: CompareOp::Eq,
|
op: CompareOp::Eq,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _exit_shape_trim = if exit_args.is_empty() {
|
let _exit_shape_trim = if ctx.exit_args.is_empty() {
|
||||||
LoopExitShape::new_manual(vec![e_loop])
|
LoopExitShape::new_manual(vec![e_loop])
|
||||||
} else {
|
} else {
|
||||||
LoopExitShape::new_manual(exit_args.clone())
|
LoopExitShape::new_manual(ctx.exit_args.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Jump {
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
@ -794,67 +726,26 @@ pub fn lower_case_a_loop_to_joinir_for_append_defs_minimal(
|
|||||||
query: &impl MirQuery,
|
query: &impl MirQuery,
|
||||||
mir_func: &MirFunction,
|
mir_func: &MirFunction,
|
||||||
) -> Option<JoinModule> {
|
) -> Option<JoinModule> {
|
||||||
if loop_form.header == loop_form.exit {
|
// CaseAContext で共通ロジックを実行
|
||||||
eprintln!(
|
let ctx = CaseAContext::new(
|
||||||
"[joinir/generic_case_a/append_defs] loop_form malformed (header == exit), fallback"
|
loop_form,
|
||||||
);
|
var_classes,
|
||||||
return None;
|
exit_live,
|
||||||
}
|
query,
|
||||||
|
mir_func,
|
||||||
|
"append_defs",
|
||||||
|
|offset| value_id_ranges::funcscanner_append_defs::loop_step(offset),
|
||||||
|
)?;
|
||||||
|
|
||||||
let LoopFormIntake {
|
let dst_key = ctx.pinned_name_or_first(0)?;
|
||||||
pinned_ordered: ordered_pinned,
|
let defs_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| dst_key.clone());
|
||||||
carrier_ordered: ordered_carriers,
|
let n_key = ctx.pinned_name_or_first(2).unwrap_or_else(|| defs_key.clone());
|
||||||
name_to_header_id: _name_to_id,
|
let i_key = ctx.carrier_name_or_first(0)?;
|
||||||
header_snapshot,
|
|
||||||
exit_snapshots,
|
|
||||||
exit_preds: _exit_preds,
|
|
||||||
} = intake_loop_form(loop_form, var_classes, query, mir_func)?;
|
|
||||||
|
|
||||||
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
|
let dst_loop = ctx.get_loop_id(&dst_key)?;
|
||||||
let mut offset = 0;
|
let defs_loop = ctx.get_loop_id(&defs_key)?;
|
||||||
for name in &ordered_pinned {
|
let n_loop = ctx.get_loop_id(&n_key)?;
|
||||||
name_to_loop_id.insert(
|
let i_loop = ctx.get_loop_id(&i_key)?;
|
||||||
name.clone(),
|
|
||||||
value_id_ranges::funcscanner_append_defs::loop_step(offset),
|
|
||||||
);
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
for name in &ordered_carriers {
|
|
||||||
name_to_loop_id.insert(
|
|
||||||
name.clone(),
|
|
||||||
value_id_ranges::funcscanner_append_defs::loop_step(offset),
|
|
||||||
);
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinned_ids: Vec<ValueId> = ordered_pinned
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
let carrier_ids: Vec<ValueId> = ordered_carriers
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let exit_live_set: BTreeSet<String> =
|
|
||||||
exit_live.compute_live_at_exit(query, loop_form.exit, &header_snapshot, &exit_snapshots);
|
|
||||||
let exit_args = resolve_exit_args(&exit_live_set, &name_to_loop_id, &ordered_carriers)?;
|
|
||||||
|
|
||||||
let dst_key = ordered_pinned.get(0).cloned()?;
|
|
||||||
let defs_key = ordered_pinned
|
|
||||||
.get(1)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| dst_key.clone());
|
|
||||||
let n_key = ordered_pinned
|
|
||||||
.get(2)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| defs_key.clone());
|
|
||||||
let i_key = ordered_carriers.get(0).cloned()?;
|
|
||||||
|
|
||||||
let dst_loop = *name_to_loop_id.get(&dst_key)?;
|
|
||||||
let defs_loop = *name_to_loop_id.get(&defs_key)?;
|
|
||||||
let n_loop = *name_to_loop_id.get(&n_key)?;
|
|
||||||
let i_loop = *name_to_loop_id.get(&i_key)?;
|
|
||||||
|
|
||||||
let mut join_module = JoinModule::new();
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
@ -882,9 +773,10 @@ pub fn lower_case_a_loop_to_joinir_for_append_defs_minimal(
|
|||||||
entry_name_to_id.insert("n".to_string(), n_param);
|
entry_name_to_id.insert("n".to_string(), n_param);
|
||||||
entry_name_to_id.insert("i".to_string(), i_init);
|
entry_name_to_id.insert("i".to_string(), i_init);
|
||||||
|
|
||||||
let loop_call_args: Vec<ValueId> = ordered_pinned
|
let loop_call_args: Vec<ValueId> = ctx
|
||||||
|
.ordered_pinned
|
||||||
.iter()
|
.iter()
|
||||||
.chain(ordered_carriers.iter())
|
.chain(ctx.ordered_carriers.iter())
|
||||||
.map(|name| entry_name_to_id.get(name).copied())
|
.map(|name| entry_name_to_id.get(name).copied())
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
@ -900,7 +792,7 @@ pub fn lower_case_a_loop_to_joinir_for_append_defs_minimal(
|
|||||||
join_module.add_function(entry_func);
|
join_module.add_function(entry_func);
|
||||||
|
|
||||||
// loop_step(dst, defs_box, n, i)
|
// loop_step(dst, defs_box, n, i)
|
||||||
let header_shape = LoopHeaderShape::new_manual(pinned_ids.clone(), carrier_ids.clone());
|
let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone());
|
||||||
let loop_params = header_shape.to_loop_step_params();
|
let loop_params = header_shape.to_loop_step_params();
|
||||||
let mut loop_step_func =
|
let mut loop_step_func =
|
||||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
||||||
@ -915,11 +807,11 @@ pub fn lower_case_a_loop_to_joinir_for_append_defs_minimal(
|
|||||||
rhs: n_loop,
|
rhs: n_loop,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _exit_shape = LoopExitShape::new_manual(exit_args.clone());
|
let _exit_shape = LoopExitShape::new_manual(ctx.exit_args.clone());
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Jump {
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
cont: JoinContId::new(0),
|
cont: JoinContId::new(0),
|
||||||
args: exit_args.clone(),
|
args: ctx.exit_args.clone(),
|
||||||
cond: Some(cmp_result),
|
cond: Some(cmp_result),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -988,69 +880,30 @@ pub fn lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal(
|
|||||||
query: &impl MirQuery,
|
query: &impl MirQuery,
|
||||||
mir_func: &MirFunction,
|
mir_func: &MirFunction,
|
||||||
) -> Option<JoinModule> {
|
) -> Option<JoinModule> {
|
||||||
if loop_form.header == loop_form.exit {
|
// CaseAContext で共通ロジックを実行
|
||||||
eprintln!("[joinir/generic_case_a/stage1] loop_form malformed (header == exit), fallback");
|
let ctx = CaseAContext::new(
|
||||||
return None;
|
loop_form,
|
||||||
}
|
var_classes,
|
||||||
|
exit_live,
|
||||||
|
query,
|
||||||
|
mir_func,
|
||||||
|
"stage1",
|
||||||
|
|offset| stage1_vid::loop_step(offset),
|
||||||
|
)?;
|
||||||
|
|
||||||
let LoopFormIntake {
|
let entries_key = ctx.pinned_name_or_first(0)?;
|
||||||
pinned_ordered: ordered_pinned,
|
let n_key = ctx.pinned_name_or_first(1).unwrap_or_else(|| entries_key.clone());
|
||||||
carrier_ordered: ordered_carriers,
|
let modules_key = ctx.pinned_name_or_first(2).unwrap_or_else(|| entries_key.clone());
|
||||||
name_to_header_id: _name_to_id,
|
let seen_key = ctx.pinned_name_or_first(3).unwrap_or_else(|| entries_key.clone());
|
||||||
header_snapshot,
|
let prefix_key = ctx.carrier_name_or_first(0)?;
|
||||||
exit_snapshots,
|
let i_key = ctx.carrier_name_or_first(1).unwrap_or_else(|| prefix_key.clone());
|
||||||
exit_preds: _exit_preds,
|
|
||||||
} = intake_loop_form(loop_form, var_classes, query, mir_func)?;
|
|
||||||
|
|
||||||
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
|
let entries_loop = ctx.get_loop_id(&entries_key)?;
|
||||||
let mut offset = 0;
|
let n_loop = ctx.get_loop_id(&n_key)?;
|
||||||
for name in &ordered_pinned {
|
let modules_loop = ctx.get_loop_id(&modules_key)?;
|
||||||
name_to_loop_id.insert(name.clone(), stage1_vid::loop_step(offset));
|
let seen_loop = ctx.get_loop_id(&seen_key)?;
|
||||||
offset += 1;
|
let prefix_loop = ctx.get_loop_id(&prefix_key)?;
|
||||||
}
|
let i_loop = ctx.get_loop_id(&i_key)?;
|
||||||
for name in &ordered_carriers {
|
|
||||||
name_to_loop_id.insert(name.clone(), stage1_vid::loop_step(offset));
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinned_ids: Vec<ValueId> = ordered_pinned
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
let carrier_ids: Vec<ValueId> = ordered_carriers
|
|
||||||
.iter()
|
|
||||||
.filter_map(|k| name_to_loop_id.get(k).copied())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let exit_live_set: BTreeSet<String> =
|
|
||||||
exit_live.compute_live_at_exit(query, loop_form.exit, &header_snapshot, &exit_snapshots);
|
|
||||||
let exit_args = resolve_exit_args(&exit_live_set, &name_to_loop_id, &ordered_carriers)?;
|
|
||||||
|
|
||||||
let entries_key = ordered_pinned.get(0).cloned()?;
|
|
||||||
let n_key = ordered_pinned
|
|
||||||
.get(1)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| entries_key.clone());
|
|
||||||
let modules_key = ordered_pinned
|
|
||||||
.get(2)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| entries_key.clone());
|
|
||||||
let seen_key = ordered_pinned
|
|
||||||
.get(3)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| entries_key.clone());
|
|
||||||
let prefix_key = ordered_carriers.get(0).cloned()?;
|
|
||||||
let i_key = ordered_carriers
|
|
||||||
.get(1)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| prefix_key.clone());
|
|
||||||
|
|
||||||
let entries_loop = *name_to_loop_id.get(&entries_key)?;
|
|
||||||
let n_loop = *name_to_loop_id.get(&n_key)?;
|
|
||||||
let modules_loop = *name_to_loop_id.get(&modules_key)?;
|
|
||||||
let seen_loop = *name_to_loop_id.get(&seen_key)?;
|
|
||||||
let prefix_loop = *name_to_loop_id.get(&prefix_key)?;
|
|
||||||
let i_loop = *name_to_loop_id.get(&i_key)?;
|
|
||||||
|
|
||||||
let mut join_module = JoinModule::new();
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
@ -1089,9 +942,10 @@ pub fn lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal(
|
|||||||
entry_name_to_id.insert(prefix_key.clone(), prefix_param);
|
entry_name_to_id.insert(prefix_key.clone(), prefix_param);
|
||||||
entry_name_to_id.insert(i_key.clone(), i_init);
|
entry_name_to_id.insert(i_key.clone(), i_init);
|
||||||
|
|
||||||
let loop_call_args: Vec<ValueId> = ordered_pinned
|
let loop_call_args: Vec<ValueId> = ctx
|
||||||
|
.ordered_pinned
|
||||||
.iter()
|
.iter()
|
||||||
.chain(ordered_carriers.iter())
|
.chain(ctx.ordered_carriers.iter())
|
||||||
.map(|name| entry_name_to_id.get(name).copied())
|
.map(|name| entry_name_to_id.get(name).copied())
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
@ -1107,7 +961,7 @@ pub fn lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal(
|
|||||||
join_module.add_function(resolve_func);
|
join_module.add_function(resolve_func);
|
||||||
|
|
||||||
// loop_step(entries, n, modules, seen, prefix, i)
|
// loop_step(entries, n, modules, seen, prefix, i)
|
||||||
let header_shape = LoopHeaderShape::new_manual(pinned_ids.clone(), carrier_ids.clone());
|
let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone());
|
||||||
let loop_params = header_shape.to_loop_step_params();
|
let loop_params = header_shape.to_loop_step_params();
|
||||||
let mut loop_step_func =
|
let mut loop_step_func =
|
||||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_params.clone());
|
||||||
@ -1122,10 +976,10 @@ pub fn lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal(
|
|||||||
rhs: n_loop,
|
rhs: n_loop,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let exit_shape = if exit_args.is_empty() {
|
let exit_shape = if ctx.exit_args.is_empty() {
|
||||||
LoopExitShape::new_manual(vec![prefix_loop])
|
LoopExitShape::new_manual(vec![prefix_loop])
|
||||||
} else {
|
} else {
|
||||||
LoopExitShape::new_manual(exit_args.clone())
|
LoopExitShape::new_manual(ctx.exit_args.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Jump {
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
|||||||
@ -14,7 +14,6 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||||||
pub(crate) struct LoopFormIntake {
|
pub(crate) struct LoopFormIntake {
|
||||||
pub pinned_ordered: Vec<String>,
|
pub pinned_ordered: Vec<String>,
|
||||||
pub carrier_ordered: Vec<String>,
|
pub carrier_ordered: Vec<String>,
|
||||||
pub name_to_header_id: BTreeMap<String, ValueId>,
|
|
||||||
pub header_snapshot: BTreeMap<String, ValueId>,
|
pub header_snapshot: BTreeMap<String, ValueId>,
|
||||||
pub exit_snapshots: Vec<(BasicBlockId, BTreeMap<String, ValueId>)>,
|
pub exit_snapshots: Vec<(BasicBlockId, BTreeMap<String, ValueId>)>,
|
||||||
pub exit_preds: Vec<BasicBlockId>,
|
pub exit_preds: Vec<BasicBlockId>,
|
||||||
@ -204,7 +203,6 @@ pub(crate) fn intake_loop_form(
|
|||||||
Some(LoopFormIntake {
|
Some(LoopFormIntake {
|
||||||
pinned_ordered: ordered_pinned,
|
pinned_ordered: ordered_pinned,
|
||||||
carrier_ordered: ordered_carriers,
|
carrier_ordered: ordered_carriers,
|
||||||
name_to_header_id: header_vals_mir.clone(),
|
|
||||||
header_snapshot: header_vals_mir,
|
header_snapshot: header_vals_mir,
|
||||||
exit_snapshots,
|
exit_snapshots,
|
||||||
exit_preds,
|
exit_preds,
|
||||||
|
|||||||
499
src/mir/join_ir/lowering/loop_scope_shape.rs
Normal file
499
src/mir/join_ir/lowering/loop_scope_shape.rs
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
//! LoopScopeShape - ループ変数スコープの統合ビュー
|
||||||
|
//!
|
||||||
|
//! # Purpose
|
||||||
|
//!
|
||||||
|
//! 既存の3箱(LoopVarClassBox, LoopExitLivenessBox, LocalScopeInspectorBox)の
|
||||||
|
//! 情報を1つの構造体に統合し、JoinIR lowering が参照する唯一のソースにする。
|
||||||
|
//!
|
||||||
|
//! # Design Philosophy (Box-First)
|
||||||
|
//!
|
||||||
|
//! Phase 29/30 で以下の責務分離を実現:
|
||||||
|
//! - LoopForm: ループの「形」(preheader/header/body/latch/exit)
|
||||||
|
//! - LoopScopeShape: 変数の「役割」(pinned/carrier/body_local/exit_live)
|
||||||
|
//! - JoinIR: 関数と継続で表現された制御構造
|
||||||
|
//!
|
||||||
|
//! # Phase 29 Strategy
|
||||||
|
//!
|
||||||
|
//! 現在は「既存箱を呼ぶだけの薄いラッパー」として実装。
|
||||||
|
//! 将来は既存箱を吸収して LoopScopeShape を唯一のソースにする(Phase 30)。
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//!
|
||||||
|
//! ```ignore
|
||||||
|
//! let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
//! &loop_form_intake,
|
||||||
|
//! &var_classes,
|
||||||
|
//! &exit_live,
|
||||||
|
//! query,
|
||||||
|
//! )?;
|
||||||
|
//!
|
||||||
|
//! // JoinIR lowering では scope のフィールドを参照
|
||||||
|
//! for name in &scope.pinned {
|
||||||
|
//! // ...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
|
use crate::mir::join_ir::lowering::exit_args_resolver::resolve_exit_args;
|
||||||
|
use crate::mir::join_ir::lowering::loop_form_intake::{intake_loop_form, LoopFormIntake};
|
||||||
|
use crate::mir::loop_form::LoopForm;
|
||||||
|
use crate::mir::phi_core::loop_exit_liveness::LoopExitLivenessBox;
|
||||||
|
use crate::mir::phi_core::loop_var_classifier::{LoopVarClass, LoopVarClassBox};
|
||||||
|
use crate::mir::{BasicBlockId, MirFunction, MirQuery, ValueId};
|
||||||
|
|
||||||
|
/// ループ変数スコープの統合ビュー
|
||||||
|
///
|
||||||
|
/// # Fields
|
||||||
|
///
|
||||||
|
/// - `pinned`: ループ外から来て変わらない変数(header/exit PHI 両方必要)
|
||||||
|
/// - `carriers`: 各イテレーションで更新される変数(header/exit PHI 両方必要)
|
||||||
|
/// - `body_locals`: ループ内だけで完結する変数(JoinIR では参照しない)
|
||||||
|
/// - `exit_live`: ループ後に使われる変数(needs_exit_phi() == true)
|
||||||
|
/// - `progress_carrier`: ループを前に進める変数(将来の Verifier 用)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct LoopScopeShape {
|
||||||
|
/// Loop-crossing parameters (always need header/exit PHI)
|
||||||
|
pub pinned: BTreeSet<String>,
|
||||||
|
|
||||||
|
/// Loop-modified variables (always need header/exit PHI)
|
||||||
|
pub carriers: BTreeSet<String>,
|
||||||
|
|
||||||
|
/// Body-local variables (JoinIR doesn't need these)
|
||||||
|
/// Includes both BodyLocalExit and BodyLocalInternal
|
||||||
|
pub body_locals: BTreeSet<String>,
|
||||||
|
|
||||||
|
/// Variables live at exit (needs_exit_phi() == true)
|
||||||
|
/// = pinned ∪ carriers ∪ BodyLocalExit
|
||||||
|
pub exit_live: BTreeSet<String>,
|
||||||
|
|
||||||
|
/// Progress carrier for loop termination check (future Verifier use)
|
||||||
|
/// Typically the loop index variable (i, pos, etc.)
|
||||||
|
pub progress_carrier: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoopScopeShape {
|
||||||
|
/// Create LoopScopeShape from existing boxes (Phase 29: thin wrapper)
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `intake`: LoopFormIntake containing classified variable info
|
||||||
|
/// - `var_classes`: LoopVarClassBox for classification
|
||||||
|
/// - `exit_live_box`: LoopExitLivenessBox for exit liveness
|
||||||
|
/// - `query`: MirQuery for liveness computation
|
||||||
|
/// - `exit_block`: Exit block ID
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Some(LoopScopeShape) if successful, None if critical data is missing.
|
||||||
|
///
|
||||||
|
/// # Phase 29 Behavior
|
||||||
|
///
|
||||||
|
/// Currently just reads from existing boxes without modifying behavior.
|
||||||
|
/// Future Phase 30 will absorb the boxes entirely.
|
||||||
|
pub(crate) fn from_existing_boxes(
|
||||||
|
intake: &LoopFormIntake,
|
||||||
|
var_classes: &LoopVarClassBox,
|
||||||
|
exit_live_box: &LoopExitLivenessBox,
|
||||||
|
query: &impl MirQuery,
|
||||||
|
exit_block: BasicBlockId,
|
||||||
|
) -> Option<Self> {
|
||||||
|
// Extract pinned and carriers from intake (already classified)
|
||||||
|
let pinned: BTreeSet<String> = intake.pinned_ordered.iter().cloned().collect();
|
||||||
|
let carriers: BTreeSet<String> = intake.carrier_ordered.iter().cloned().collect();
|
||||||
|
|
||||||
|
// Build LocalScopeInspectorBox for body_local classification
|
||||||
|
let mut inspector =
|
||||||
|
crate::mir::phi_core::local_scope_inspector::LocalScopeInspectorBox::new();
|
||||||
|
inspector.record_snapshot(exit_block, &intake.header_snapshot);
|
||||||
|
for (bb, snap) in &intake.exit_snapshots {
|
||||||
|
inspector.record_snapshot(*bb, snap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classify all variables to find body_locals
|
||||||
|
let all_names: Vec<String> = intake.header_snapshot.keys().cloned().collect();
|
||||||
|
let classified = var_classes.classify_all(
|
||||||
|
&all_names,
|
||||||
|
&intake.pinned_ordered,
|
||||||
|
&intake.carrier_ordered,
|
||||||
|
&inspector,
|
||||||
|
&intake.exit_preds,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut body_locals: BTreeSet<String> = BTreeSet::new();
|
||||||
|
let mut exit_live: BTreeSet<String> = BTreeSet::new();
|
||||||
|
|
||||||
|
for (name, class) in &classified {
|
||||||
|
match class {
|
||||||
|
LoopVarClass::Pinned => {
|
||||||
|
exit_live.insert(name.clone());
|
||||||
|
}
|
||||||
|
LoopVarClass::Carrier => {
|
||||||
|
exit_live.insert(name.clone());
|
||||||
|
}
|
||||||
|
LoopVarClass::BodyLocalExit => {
|
||||||
|
body_locals.insert(name.clone());
|
||||||
|
exit_live.insert(name.clone());
|
||||||
|
}
|
||||||
|
LoopVarClass::BodyLocalInternal => {
|
||||||
|
body_locals.insert(name.clone());
|
||||||
|
// NOT in exit_live (Option C: skip exit PHI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute exit_live from LoopExitLivenessBox (Phase 1: usually empty)
|
||||||
|
let exit_live_from_box = exit_live_box.compute_live_at_exit(
|
||||||
|
query,
|
||||||
|
exit_block,
|
||||||
|
&intake.header_snapshot,
|
||||||
|
&intake.exit_snapshots,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge: exit_live = (classification-based) ∪ (liveness-box-based)
|
||||||
|
for name in exit_live_from_box {
|
||||||
|
exit_live.insert(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine progress_carrier (heuristic: first carrier, typically 'i')
|
||||||
|
let progress_carrier = carriers.iter().next().cloned();
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
pinned,
|
||||||
|
carriers,
|
||||||
|
body_locals,
|
||||||
|
exit_live,
|
||||||
|
progress_carrier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a variable needs header PHI
|
||||||
|
pub fn needs_header_phi(&self, var_name: &str) -> bool {
|
||||||
|
self.pinned.contains(var_name) || self.carriers.contains(var_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a variable needs exit PHI
|
||||||
|
pub fn needs_exit_phi(&self, var_name: &str) -> bool {
|
||||||
|
self.exit_live.contains(var_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get ordered pinned variables (for JoinIR parameter generation)
|
||||||
|
pub fn pinned_ordered(&self) -> Vec<String> {
|
||||||
|
self.pinned.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get ordered carrier variables (for JoinIR parameter generation)
|
||||||
|
pub fn carriers_ordered(&self) -> Vec<String> {
|
||||||
|
self.carriers.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all variables that need header PHI (pinned + carriers)
|
||||||
|
pub fn header_phi_vars(&self) -> Vec<String> {
|
||||||
|
let mut result: Vec<String> = self.pinned.iter().cloned().collect();
|
||||||
|
result.extend(self.carriers.iter().cloned());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all variables that need exit PHI
|
||||||
|
pub fn exit_phi_vars(&self) -> Vec<String> {
|
||||||
|
self.exit_live.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CaseAContext - generic_case_a 共通ロジックの集約
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/// Case A lowering の共通コンテキスト
|
||||||
|
///
|
||||||
|
/// generic_case_a.rs の4関数に共通するロジックを集約し、
|
||||||
|
/// 約200行の重複コードを削減する。
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// let ctx = CaseAContext::new(
|
||||||
|
/// loop_form, var_classes, exit_live, query, mir_func,
|
||||||
|
/// "skip_ws",
|
||||||
|
/// |offset| vid::loop_step(offset),
|
||||||
|
/// )?;
|
||||||
|
///
|
||||||
|
/// // ctx から必要な情報を取り出して JoinModule を構築
|
||||||
|
/// let header_shape = LoopHeaderShape::new_manual(ctx.pinned_ids.clone(), ctx.carrier_ids.clone());
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct CaseAContext {
|
||||||
|
/// LoopScopeShape(変数スコープ情報)
|
||||||
|
pub scope: LoopScopeShape,
|
||||||
|
|
||||||
|
/// 順序付き pinned 変数名
|
||||||
|
pub ordered_pinned: Vec<String>,
|
||||||
|
|
||||||
|
/// 順序付き carrier 変数名
|
||||||
|
pub ordered_carriers: Vec<String>,
|
||||||
|
|
||||||
|
/// 変数名 → ループ関数内 ValueId のマッピング
|
||||||
|
pub name_to_loop_id: BTreeMap<String, ValueId>,
|
||||||
|
|
||||||
|
/// pinned 変数の ValueId 列
|
||||||
|
pub pinned_ids: Vec<ValueId>,
|
||||||
|
|
||||||
|
/// carrier 変数の ValueId 列
|
||||||
|
pub carrier_ids: Vec<ValueId>,
|
||||||
|
|
||||||
|
/// exit 時に渡す引数の ValueId 列
|
||||||
|
pub exit_args: Vec<ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CaseAContext {
|
||||||
|
/// CaseAContext を構築する
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `loop_form`: ループ構造情報
|
||||||
|
/// - `var_classes`: 変数分類器
|
||||||
|
/// - `exit_live`: exit liveness 情報
|
||||||
|
/// - `query`: MIR クエリ
|
||||||
|
/// - `mir_func`: MIR 関数
|
||||||
|
/// - `log_tag`: ログ出力用タグ(例: "skip_ws", "trim")
|
||||||
|
/// - `loop_step_id_fn`: offset から ValueId を生成する関数
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Some(CaseAContext) if successful, None if validation fails or data is missing.
|
||||||
|
pub(crate) fn new<F>(
|
||||||
|
loop_form: &LoopForm,
|
||||||
|
var_classes: &LoopVarClassBox,
|
||||||
|
exit_live: &LoopExitLivenessBox,
|
||||||
|
query: &impl MirQuery,
|
||||||
|
mir_func: &MirFunction,
|
||||||
|
log_tag: &str,
|
||||||
|
loop_step_id_fn: F,
|
||||||
|
) -> Option<Self>
|
||||||
|
where
|
||||||
|
F: Fn(u32) -> ValueId,
|
||||||
|
{
|
||||||
|
// 1) LoopForm validation
|
||||||
|
if loop_form.header == loop_form.exit {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/generic_case_a/{}] loop_form malformed (header == exit), fallback",
|
||||||
|
log_tag
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) MIR から pinned/carrier/exit 情報を抽出
|
||||||
|
let intake = intake_loop_form(loop_form, var_classes, query, mir_func)?;
|
||||||
|
|
||||||
|
// 3) LoopScopeShape を構築
|
||||||
|
let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
&intake,
|
||||||
|
var_classes,
|
||||||
|
exit_live,
|
||||||
|
query,
|
||||||
|
loop_form.exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let ordered_pinned = scope.pinned_ordered();
|
||||||
|
let ordered_carriers = scope.carriers_ordered();
|
||||||
|
|
||||||
|
// 4) 変数名 → ValueId マッピングを構築
|
||||||
|
let mut name_to_loop_id: BTreeMap<String, ValueId> = BTreeMap::new();
|
||||||
|
let mut offset: u32 = 0;
|
||||||
|
for name in &ordered_pinned {
|
||||||
|
name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset));
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
for name in &ordered_carriers {
|
||||||
|
name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset));
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) pinned_ids / carrier_ids を構築
|
||||||
|
let pinned_ids: Vec<ValueId> = ordered_pinned
|
||||||
|
.iter()
|
||||||
|
.filter_map(|k| name_to_loop_id.get(k).copied())
|
||||||
|
.collect();
|
||||||
|
let carrier_ids: Vec<ValueId> = ordered_carriers
|
||||||
|
.iter()
|
||||||
|
.filter_map(|k| name_to_loop_id.get(k).copied())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// 6) exit_args を解決
|
||||||
|
let exit_args = resolve_exit_args(&scope.exit_live, &name_to_loop_id, &ordered_carriers)?;
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
scope,
|
||||||
|
ordered_pinned,
|
||||||
|
ordered_carriers,
|
||||||
|
name_to_loop_id,
|
||||||
|
pinned_ids,
|
||||||
|
carrier_ids,
|
||||||
|
exit_args,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 変数名から loop 関数内の ValueId を取得
|
||||||
|
pub fn get_loop_id(&self, name: &str) -> Option<ValueId> {
|
||||||
|
self.name_to_loop_id.get(name).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pinned 変数の n 番目の名前を取得(なければ 0 番目を使う)
|
||||||
|
pub fn pinned_name_or_first(&self, index: usize) -> Option<String> {
|
||||||
|
self.ordered_pinned
|
||||||
|
.get(index)
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| self.ordered_pinned.first().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// carrier 変数の n 番目の名前を取得(なければ 0 番目を使う)
|
||||||
|
pub fn carrier_name_or_first(&self, index: usize) -> Option<String> {
|
||||||
|
self.ordered_carriers
|
||||||
|
.get(index)
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| self.ordered_carriers.first().cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Unit Tests
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
fn make_dummy_intake() -> LoopFormIntake {
|
||||||
|
let mut header_snapshot = BTreeMap::new();
|
||||||
|
header_snapshot.insert("s".to_string(), ValueId(10));
|
||||||
|
header_snapshot.insert("n".to_string(), ValueId(20));
|
||||||
|
header_snapshot.insert("i".to_string(), ValueId(30));
|
||||||
|
|
||||||
|
LoopFormIntake {
|
||||||
|
pinned_ordered: vec!["s".to_string(), "n".to_string()],
|
||||||
|
carrier_ordered: vec!["i".to_string()],
|
||||||
|
header_snapshot,
|
||||||
|
exit_snapshots: vec![],
|
||||||
|
exit_preds: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmptyQuery;
|
||||||
|
impl MirQuery for EmptyQuery {
|
||||||
|
fn insts_in_block(&self, _bb: BasicBlockId) -> &[crate::mir::MirInstruction] {
|
||||||
|
&[]
|
||||||
|
}
|
||||||
|
fn succs(&self, _bb: BasicBlockId) -> Vec<BasicBlockId> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
fn reads_of(&self, _inst: &crate::mir::MirInstruction) -> Vec<ValueId> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
fn writes_of(&self, _inst: &crate::mir::MirInstruction) -> Vec<ValueId> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_existing_boxes_basic() {
|
||||||
|
let intake = make_dummy_intake();
|
||||||
|
let var_classes = LoopVarClassBox::new();
|
||||||
|
let exit_live_box = LoopExitLivenessBox::new();
|
||||||
|
let query = EmptyQuery;
|
||||||
|
|
||||||
|
let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
&intake,
|
||||||
|
&var_classes,
|
||||||
|
&exit_live_box,
|
||||||
|
&query,
|
||||||
|
BasicBlockId::new(100),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(scope.is_some());
|
||||||
|
let scope = scope.unwrap();
|
||||||
|
|
||||||
|
// pinned should be {s, n}
|
||||||
|
assert!(scope.pinned.contains("s"));
|
||||||
|
assert!(scope.pinned.contains("n"));
|
||||||
|
assert_eq!(scope.pinned.len(), 2);
|
||||||
|
|
||||||
|
// carriers should be {i}
|
||||||
|
assert!(scope.carriers.contains("i"));
|
||||||
|
assert_eq!(scope.carriers.len(), 1);
|
||||||
|
|
||||||
|
// progress_carrier should be "i"
|
||||||
|
assert_eq!(scope.progress_carrier, Some("i".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_needs_header_phi() {
|
||||||
|
let intake = make_dummy_intake();
|
||||||
|
let var_classes = LoopVarClassBox::new();
|
||||||
|
let exit_live_box = LoopExitLivenessBox::new();
|
||||||
|
let query = EmptyQuery;
|
||||||
|
|
||||||
|
let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
&intake,
|
||||||
|
&var_classes,
|
||||||
|
&exit_live_box,
|
||||||
|
&query,
|
||||||
|
BasicBlockId::new(100),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(scope.needs_header_phi("s"));
|
||||||
|
assert!(scope.needs_header_phi("n"));
|
||||||
|
assert!(scope.needs_header_phi("i"));
|
||||||
|
assert!(!scope.needs_header_phi("unknown"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_needs_exit_phi() {
|
||||||
|
let intake = make_dummy_intake();
|
||||||
|
let var_classes = LoopVarClassBox::new();
|
||||||
|
let exit_live_box = LoopExitLivenessBox::new();
|
||||||
|
let query = EmptyQuery;
|
||||||
|
|
||||||
|
let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
&intake,
|
||||||
|
&var_classes,
|
||||||
|
&exit_live_box,
|
||||||
|
&query,
|
||||||
|
BasicBlockId::new(100),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Pinned and Carrier should need exit PHI
|
||||||
|
assert!(scope.needs_exit_phi("s"));
|
||||||
|
assert!(scope.needs_exit_phi("n"));
|
||||||
|
assert!(scope.needs_exit_phi("i"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ordered_accessors() {
|
||||||
|
let intake = make_dummy_intake();
|
||||||
|
let var_classes = LoopVarClassBox::new();
|
||||||
|
let exit_live_box = LoopExitLivenessBox::new();
|
||||||
|
let query = EmptyQuery;
|
||||||
|
|
||||||
|
let scope = LoopScopeShape::from_existing_boxes(
|
||||||
|
&intake,
|
||||||
|
&var_classes,
|
||||||
|
&exit_live_box,
|
||||||
|
&query,
|
||||||
|
BasicBlockId::new(100),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let pinned = scope.pinned_ordered();
|
||||||
|
assert_eq!(pinned.len(), 2);
|
||||||
|
assert!(pinned.contains(&"s".to_string()));
|
||||||
|
assert!(pinned.contains(&"n".to_string()));
|
||||||
|
|
||||||
|
let carriers = scope.carriers_ordered();
|
||||||
|
assert_eq!(carriers.len(), 1);
|
||||||
|
assert!(carriers.contains(&"i".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,6 +19,7 @@ pub mod funcscanner_append_defs;
|
|||||||
pub mod funcscanner_trim;
|
pub mod funcscanner_trim;
|
||||||
pub mod generic_case_a;
|
pub mod generic_case_a;
|
||||||
pub mod loop_form_intake;
|
pub mod loop_form_intake;
|
||||||
|
pub mod loop_scope_shape;
|
||||||
pub mod min_loop;
|
pub mod min_loop;
|
||||||
pub mod skip_ws;
|
pub mod skip_ws;
|
||||||
pub mod stage1_using_resolver;
|
pub mod stage1_using_resolver;
|
||||||
|
|||||||
Reference in New Issue
Block a user