chore: Phase 25.2関連ドキュメント更新&レガシーテストアーカイブ整理
## ドキュメント更新 - CURRENT_TASK.md: Phase 25.2完了記録 - phase-25.1b/e/q/25.2 README更新 - json_v0_bridge/README.md新規追加 ## テストファイル整理 - vtable_*テストをtests/archive/に移動(6ファイル) - json_program_loop.rsテスト追加 ## コード整理 - プラグイン(egui/python-compiler)微修正 - benchmarks.rs, instance_v2.rs更新 - MIR関連ファイル微調整 ## 全体成果 Phase 25.2完了により: - LoopSnapshotMergeBox統一管理実装 - ValueId(1283)バグ根本解決 - ~35行コード削減(目標210行の16%) - 11テスト全部PASS、3実行テストケースPASS
This commit is contained in:
9
src/runner/json_v0_bridge/README.md
Normal file
9
src/runner/json_v0_bridge/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
## JSON v0 Bridge
|
||||
|
||||
- 役割: Stage‑B / self‑host 側で生成した `Program(JSON v0)` を、Rust 側の `LoopFormBuilder + LoopSnapshotMergeBox` に渡して MIR を生成する薄いフロント。
|
||||
- 責務:
|
||||
1. JSON を `ProgramV0` にデシリアライズし、`lower_stmt_with_vars` / `loop_.rs` などへ流す。
|
||||
2. ループについては `LoopFormJsonOps` を介して preheader/header/body/latch/continue_merge/exit のブロック ID とスナップショットを用意し、PHI 構築は LoopForm 側に委譲する。
|
||||
3. break / continue / exit の snapshot を `LoopSnapshotMergeBox` に渡して canonical continue_merge/backedge を構築する。
|
||||
|
||||
LoopForm/PHI の意味論を変更したい場合は、`loopform_builder.rs` / `loop_snapshot_merge.rs` を更新すること。`loop_.rs` 内での ad-hoc な PHI 実装は禁止。
|
||||
@ -21,8 +21,14 @@ pub(super) mod throw_ctx; // thread-local ctx for Result-mode throw routing
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct LoopContext {
|
||||
/// ループ条件を評価する header ブロック
|
||||
pub(super) cond_bb: BasicBlockId,
|
||||
/// break がジャンプする exit ブロック
|
||||
pub(super) exit_bb: BasicBlockId,
|
||||
/// canonical continue merge ブロック(存在する場合)
|
||||
/// - Some(continue_merge_bb): continue は一度ここに集約してから header へ戻る
|
||||
/// - None: 旧来どおり header へ直接 continue
|
||||
pub(super) continue_merge_bb: Option<BasicBlockId>,
|
||||
}
|
||||
|
||||
// Snapshot stacks for loop break/continue (per-nested-loop frame)
|
||||
@ -234,8 +240,9 @@ fn lower_break_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, exit_bb: BasicBlo
|
||||
// );
|
||||
}
|
||||
|
||||
fn lower_continue_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, cond_bb: BasicBlockId) {
|
||||
jump_with_pred(f, cur_bb, cond_bb);
|
||||
fn lower_continue_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, target_bb: BasicBlockId) {
|
||||
// target_bb は header か canonical continue_merge_bb のいずれか
|
||||
jump_with_pred(f, cur_bb, target_bb);
|
||||
// ARCHIVED: JIT events moved to archive/jit-cranelift/ during Phase 15
|
||||
// crate::jit::events::emit_lower(
|
||||
// serde_json::json!({ "id": "loop_continue","cond_bb": cond_bb.0,"decision": "lower" }),
|
||||
@ -305,7 +312,8 @@ pub(super) fn lower_stmt_with_vars(
|
||||
}
|
||||
// snapshot variables at continue (after increment)
|
||||
record_continue_snapshot(cur_bb, vars);
|
||||
lower_continue_stmt(f, cur_bb, ctx.cond_bb);
|
||||
let target = ctx.continue_merge_bb.unwrap_or(ctx.cond_bb);
|
||||
lower_continue_stmt(f, cur_bb, target);
|
||||
}
|
||||
Ok(cur_bb)
|
||||
}
|
||||
|
||||
@ -1,9 +1,181 @@
|
||||
/*!
|
||||
* JSON v0 Loop Lowering Front
|
||||
*
|
||||
* Phase 25.1q 方針:
|
||||
* - ループ構造 / PHI / スナップショットの意味論は
|
||||
* `phi_core::loopform_builder::LoopFormBuilder` +
|
||||
* `phi_core::loop_snapshot_merge::LoopSnapshotMergeBox`
|
||||
* 側に SSOT として集約する。
|
||||
* - このファイルは「JSON v0 → LoopForm v2」への薄いフロントとし、
|
||||
* ループ意味論や PHI 生成ロジックをここで新規実装しない。
|
||||
*
|
||||
* 設計ガード:
|
||||
* - ループのブロック構造・PHI・スナップショットのマージ方針を
|
||||
* 変更したい場合は、必ず `loopform_builder.rs` /
|
||||
* `loop_snapshot_merge.rs` を修正すること。
|
||||
* - `loop_.rs` 側では:
|
||||
* - ブロック ID 準備
|
||||
* - 変数マップ / snapshot の受け渡し
|
||||
* - LoopFormOps 実装を通じた呼び出し
|
||||
* だけを行う。
|
||||
*/
|
||||
|
||||
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
||||
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
||||
use std::collections::HashMap;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::super::ast::ExprV0;
|
||||
|
||||
/// LoopForm v2 用の JSON bridge 実装。
|
||||
///
|
||||
/// - MirFunction 上で直接動作し、LoopFormBuilder が要求する最小限の
|
||||
/// インターフェースだけを提供する。
|
||||
/// - 「値の割り当て」「PHI の挿入」「変数マップの更新」「スナップショット参照」
|
||||
/// だけを担当し、ループ意味論そのものは持たない。
|
||||
struct LoopFormJsonOps<'a> {
|
||||
f: &'a mut MirFunction,
|
||||
vars: &'a mut HashMap<String, ValueId>,
|
||||
block_var_maps: &'a mut HashMap<BasicBlockId, HashMap<String, ValueId>>,
|
||||
current_block: BasicBlockId,
|
||||
}
|
||||
|
||||
impl<'a> LoopFormJsonOps<'a> {
|
||||
fn new(
|
||||
f: &'a mut MirFunction,
|
||||
vars: &'a mut HashMap<String, ValueId>,
|
||||
block_var_maps: &'a mut HashMap<BasicBlockId, HashMap<String, ValueId>>,
|
||||
current_block: BasicBlockId,
|
||||
) -> Self {
|
||||
Self {
|
||||
f,
|
||||
vars,
|
||||
block_var_maps,
|
||||
current_block,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LoopFormOps for LoopFormJsonOps<'_> {
|
||||
fn new_value(&mut self) -> ValueId {
|
||||
// MirFunction の next_value_id を直接使う(LoopBuilder と同じポリシー)
|
||||
self.f.next_value_id()
|
||||
}
|
||||
|
||||
fn ensure_counter_after(&mut self, max_id: u32) -> Result<(), String> {
|
||||
// FunctionDefBuilder / main wrapper が params を signature に反映している前提で、
|
||||
// パラメータ数と既存 ValueId を両方考慮して next_value_id を前に進める。
|
||||
let param_count = self.f.signature.params.len() as u32;
|
||||
let min_counter = param_count.max(max_id + 1);
|
||||
if self.f.next_value_id < min_counter {
|
||||
self.f.next_value_id = min_counter;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_exists(&self, block: BasicBlockId) -> bool {
|
||||
self.f.blocks.contains_key(&block)
|
||||
}
|
||||
|
||||
fn get_block_predecessors(
|
||||
&self,
|
||||
block: BasicBlockId,
|
||||
) -> std::collections::HashSet<BasicBlockId> {
|
||||
self.f
|
||||
.blocks
|
||||
.get(&block)
|
||||
.map(|bb| bb.predecessors.clone())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn is_parameter(&self, name: &str) -> bool {
|
||||
// JSON bridge ではパラメータ名の SSOT は FunctionDefBuilder 側にある。
|
||||
// ここでは「典型的な受け口」だけを pinned 候補とし、それ以外は
|
||||
// carrier として扱う(ループ意味論上は安全)。
|
||||
//
|
||||
// - CLI entry: args
|
||||
// - インスタンスメソッド receiver: me
|
||||
matches!(name, "me" | "args")
|
||||
}
|
||||
|
||||
fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
|
||||
if !self.f.blocks.contains_key(&block) {
|
||||
return Err(format!("Block {:?} not found", block));
|
||||
}
|
||||
self.current_block = block;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_copy(&mut self, dst: ValueId, src: ValueId) -> Result<(), String> {
|
||||
if let Some(bb) = self.f.get_block_mut(self.current_block) {
|
||||
bb.add_instruction(MirInstruction::Copy { dst, src });
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Current block {:?} not found for Copy",
|
||||
self.current_block
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_jump(&mut self, target: BasicBlockId) -> Result<(), String> {
|
||||
crate::mir::ssot::cf_common::set_jump(self.f, self.current_block, target);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_phi(
|
||||
&mut self,
|
||||
dst: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(self.f, self.current_block, dst, inputs);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_phi_inputs(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
phi_id: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
if let Some(bb) = self.f.get_block_mut(block) {
|
||||
for inst in &mut bb.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs: phi_inputs } = inst {
|
||||
if *dst == phi_id {
|
||||
*phi_inputs = inputs;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(format!(
|
||||
"PHI instruction {:?} not found in block {:?}",
|
||||
phi_id, block
|
||||
))
|
||||
} else {
|
||||
Err(format!("Block {:?} not found while updating PHI", block))
|
||||
}
|
||||
}
|
||||
|
||||
fn update_var(&mut self, name: String, value: ValueId) {
|
||||
self.vars.insert(name.clone(), value);
|
||||
// 現在ブロックのスナップショットも更新しておく(get_variable_at_block 用)
|
||||
self.block_var_maps
|
||||
.entry(self.current_block)
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(name, value);
|
||||
}
|
||||
|
||||
fn get_variable_at_block(&self, name: &str, block: BasicBlockId) -> Option<ValueId> {
|
||||
if let Some(map) = self.block_var_maps.get(&block) {
|
||||
if let Some(v) = map.get(name) {
|
||||
return Some(*v);
|
||||
}
|
||||
}
|
||||
self.vars.get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_loop_stmt(
|
||||
f: &mut MirFunction,
|
||||
cur_bb: BasicBlockId,
|
||||
@ -16,209 +188,135 @@ pub(super) fn lower_loop_stmt(
|
||||
// DEBUG: Track loop lowering calls (Stage-B / JSON v0) — dev用トレース
|
||||
if std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[loop-phi/enter] lower_loop_stmt called, fn={}, base_vars={}",
|
||||
"[loop-phi/json] lower_loop_stmt called, fn={}, base_vars={}",
|
||||
f.signature.name,
|
||||
vars.len()
|
||||
);
|
||||
}
|
||||
// Unification toggle (default ON). For now legacy path is removed; when OFF, warn and proceed unified.
|
||||
|
||||
// Unification toggle(legacy フラグ。実装は LoopForm v2 に一本化済み)
|
||||
let unify_on = std::env::var("NYASH_MIR_UNIFY_LOOPFORM")
|
||||
.ok()
|
||||
.map(|v| matches!(v.trim().to_ascii_lowercase().as_str(), "1"|"true"|"on"))
|
||||
.map(|v| matches!(v.trim().to_ascii_lowercase().as_str(), "1" | "true" | "on"))
|
||||
.unwrap_or(true);
|
||||
if !unify_on {
|
||||
crate::cli_v!("[loopform] NYASH_MIR_UNIFY_LOOPFORM=0 requested, but legacy path is unavailable; using unified phi_core path");
|
||||
crate::cli_v!(
|
||||
"[loopform] NYASH_MIR_UNIFY_LOOPFORM=0 requested; JSON front still uses LoopForm v2 bridge"
|
||||
);
|
||||
}
|
||||
let cond_bb = new_block(f);
|
||||
let body_bb = new_block(f);
|
||||
let exit_bb = new_block(f);
|
||||
|
||||
// SSOT(phi_core)への統一: preheader Copy → header Phi seed を集中実装に委譲
|
||||
// 1) preheader スナップショット
|
||||
// Block layout(AST LoopBuilder に近い形に揃える)
|
||||
let preheader_bb = cur_bb;
|
||||
let header_bb = new_block(f);
|
||||
let body_bb = new_block(f);
|
||||
let latch_bb = new_block(f);
|
||||
let exit_bb = new_block(f);
|
||||
// JSON ルート用 canonical continue_merge ブロック
|
||||
let continue_merge_bb = new_block(f);
|
||||
|
||||
// 1) preheader スナップショット(Env_in(loop))
|
||||
let base_vars = vars.clone();
|
||||
let mut block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>> = HashMap::new();
|
||||
block_var_maps.insert(cur_bb, base_vars.clone());
|
||||
block_var_maps.insert(preheader_bb, base_vars.clone());
|
||||
|
||||
// 2) preheader → header へ Jump(ヘッダで Phi を組む前に制御を渡す)
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, cur_bb, cond_bb); }
|
||||
}
|
||||
// 2) LoopFormBuilder + LoopFormJsonOps を用いて preheader/header PHI を構築
|
||||
let mut loopform = LoopFormBuilder::new(preheader_bb, header_bb);
|
||||
let mut ops = LoopFormJsonOps::new(f, vars, &mut block_var_maps, preheader_bb);
|
||||
|
||||
// 3) LoopPhiOps アダプタ(準備用: preheader seed 専用)
|
||||
struct Ops<'a> {
|
||||
f: &'a mut MirFunction,
|
||||
vars: &'a mut HashMap<String, ValueId>,
|
||||
block_var_maps: &'a HashMap<BasicBlockId, HashMap<String, ValueId>>,
|
||||
}
|
||||
impl crate::mir::phi_core::loop_phi::LoopPhiOps for Ops<'_> {
|
||||
fn new_value(&mut self) -> ValueId { self.f.next_value_id() }
|
||||
fn emit_phi_at_block_start(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
dst: ValueId,
|
||||
mut inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
if let Some(bb) = self.f.get_block_mut(block) {
|
||||
// If a PHI for the same dst already exists at block start, merge inputs instead of inserting
|
||||
let mut found = false;
|
||||
// Count PHIs at head and iterate by index to allow mutation
|
||||
let phi_count = bb.phi_instructions().count();
|
||||
for i in 0..phi_count {
|
||||
if let Some(MirInstruction::Phi { dst: existing_dst, inputs: existing_inputs }) = bb.instructions.get_mut(i) {
|
||||
if *existing_dst == dst {
|
||||
// Merge: add only missing predecessors (build a set first to avoid borrow conflicts)
|
||||
let mut pred_set: std::collections::HashSet<BasicBlockId> = existing_inputs.iter().map(|(bbid, _)| *bbid).collect();
|
||||
for (pred, val) in inputs.drain(..) {
|
||||
if !pred_set.contains(&pred) {
|
||||
existing_inputs.push((pred, val));
|
||||
pred_set.insert(pred);
|
||||
}
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs });
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("block {} not found", block.0))
|
||||
}
|
||||
}
|
||||
fn update_var(&mut self, name: String, value: ValueId) { self.vars.insert(name, value); }
|
||||
fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option<ValueId> {
|
||||
self.block_var_maps.get(&block).and_then(|m| m.get(name).copied())
|
||||
}
|
||||
fn debug_verify_phi_inputs(&mut self, merge_bb: BasicBlockId, inputs: &[(BasicBlockId, ValueId)]) {
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(func) = Some(&*self.f) {
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
|
||||
}
|
||||
}
|
||||
fn emit_copy_at_preheader(
|
||||
&mut self,
|
||||
preheader_id: BasicBlockId,
|
||||
dst: ValueId,
|
||||
src: ValueId,
|
||||
) -> Result<(), String> {
|
||||
if let Some(bb) = self.f.get_block_mut(preheader_id) {
|
||||
bb.add_instruction(MirInstruction::Copy { dst, src });
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("preheader block {} not found", preheader_id.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
loopform.prepare_structure(&mut ops, &base_vars)?;
|
||||
loopform.emit_preheader(&mut ops)?;
|
||||
loopform.emit_header_phis(&mut ops)?;
|
||||
|
||||
let mut ops = Ops { f, vars, block_var_maps: &block_var_maps };
|
||||
// 4) header の不完全PHIを宣言(preheaderのコピー値でseed)し、変数マップをPHIへ再束縛
|
||||
let mut incomplete = crate::mir::phi_core::loop_phi::prepare_loop_variables_with(
|
||||
&mut ops, cond_bb, cur_bb, &base_vars,
|
||||
)?;
|
||||
// Header snapshot for exit PHIs
|
||||
let header_vars_snapshot = ops.vars.clone();
|
||||
// 3) ループ条件を header ブロックで評価し、body/exit へ分岐
|
||||
let (cval, cend) =
|
||||
super::expr::lower_expr_with_vars(env, ops.f, header_bb, cond, ops.vars)?;
|
||||
crate::mir::ssot::cf_common::set_branch(ops.f, cend, cval, body_bb, exit_bb);
|
||||
|
||||
let (cval, _cend) = super::expr::lower_expr_with_vars(env, ops.f, cond_bb, cond, ops.vars)?;
|
||||
crate::mir::ssot::cf_common::set_branch(ops.f, cond_bb, cval, body_bb, exit_bb);
|
||||
// 4) ループ本体を lowering(break/continue スナップショットは lowering.rs 側が管理)
|
||||
let mut body_vars = ops.vars.clone();
|
||||
// open snapshot frames for nested break/continue
|
||||
super::push_loop_snapshot_frames();
|
||||
loop_stack.push(LoopContext { cond_bb, exit_bb });
|
||||
// Detect simple increment hint for this loop body
|
||||
loop_stack.push(LoopContext {
|
||||
cond_bb: header_bb,
|
||||
exit_bb,
|
||||
continue_merge_bb: Some(continue_merge_bb),
|
||||
});
|
||||
super::detect_and_push_increment_hint(body);
|
||||
let bend_res = lower_stmt_list_with_vars(ops.f, body_bb, body, &mut body_vars, loop_stack, env);
|
||||
let bend_res =
|
||||
lower_stmt_list_with_vars(ops.f, body_bb, body, &mut body_vars, loop_stack, env);
|
||||
loop_stack.pop();
|
||||
let _ = super::pop_increment_hint();
|
||||
let bend = bend_res?;
|
||||
// collect snapshots for this loop level
|
||||
|
||||
// スナップショット収集(Env_out(loop) 用)
|
||||
let continue_snaps = super::pop_continue_snapshots();
|
||||
let exit_snaps = super::pop_exit_snapshots();
|
||||
// latch(body末尾)スナップショットを別マップに構築
|
||||
let mut block_var_maps2: HashMap<BasicBlockId, HashMap<String, ValueId>> = HashMap::new();
|
||||
block_var_maps2.insert(cur_bb, base_vars.clone());
|
||||
block_var_maps2.insert(bend, body_vars.clone());
|
||||
if let Some(bb) = ops.f.get_block_mut(bend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(ops.f, bend, cond_bb); }
|
||||
}
|
||||
// Detect whether the "latch" block has a backedge to the loop header/cond.
|
||||
// Note: loops with `break` often end the latch with a conditional Branch that
|
||||
// targets both `cond_bb` (continue path) and `exit_bb` (break path). We must
|
||||
// treat such branches as valid backedges as well; otherwise body-local vars
|
||||
// will miss PHI nodes at the header and become undefined on the second iteration
|
||||
// (BreakFinderBox._find_loops/2 などで観測されたバグ).
|
||||
let backedge_to_cond = match ops
|
||||
.f
|
||||
.blocks
|
||||
.get(&bend)
|
||||
.and_then(|bb| bb.terminator.as_ref())
|
||||
{
|
||||
Some(MirInstruction::Jump { target, .. }) if *target == cond_bb => true,
|
||||
Some(MirInstruction::Branch { then_bb, else_bb, .. })
|
||||
if *then_bb == cond_bb || *else_bb == cond_bb =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1");
|
||||
if trace_loop_phi {
|
||||
eprintln!("[loop-phi] backedge_to_cond={}, base_vars={}, body_vars={}",
|
||||
backedge_to_cond, base_vars.len(), body_vars.len());
|
||||
}
|
||||
if backedge_to_cond {
|
||||
// Phase 25.1c/k: body 内で新規宣言された local 変数も PHI に含める
|
||||
// BreakFinderBox._find_loops/2 等で、loop body 内の local 変数が
|
||||
// loop header に戻った時に undefined になる問題を修正
|
||||
let mut body_local_count = 0;
|
||||
for (var_name, &_latch_value) in body_vars.iter() {
|
||||
if !base_vars.contains_key(var_name) {
|
||||
// body で新規宣言された変数 → header に PHI ノードを追加
|
||||
body_local_count += 1;
|
||||
if trace_loop_phi {
|
||||
eprintln!("[loop-phi/body-local] Adding PHI for body-local var: {}", var_name);
|
||||
}
|
||||
let phi_id = ops.f.next_value_id();
|
||||
let inc_phi = crate::mir::phi_core::loop_phi::IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![], // preheader には存在しないので空
|
||||
};
|
||||
// header に空の PHI ノードを挿入(seal_incomplete_phis_with で完成させる)
|
||||
if let Some(bb) = ops.f.get_block_mut(cond_bb) {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi {
|
||||
dst: phi_id,
|
||||
inputs: vec![], // 空の PHI(後で latch/continue から完成)
|
||||
});
|
||||
}
|
||||
// 変数マップを PHI に rebind
|
||||
ops.vars.insert(var_name.clone(), phi_id);
|
||||
incomplete.push(inc_phi);
|
||||
}
|
||||
}
|
||||
if trace_loop_phi && body_local_count > 0 {
|
||||
eprintln!("[loop-phi/body-local] Total body-local vars added: {}", body_local_count);
|
||||
}
|
||||
|
||||
// 5) header の不完全PHIを完成(latch + continue スナップショット集約)
|
||||
let mut ops_seal = Ops { f: ops.f, vars: ops.vars, block_var_maps: &block_var_maps2 };
|
||||
crate::mir::phi_core::loop_phi::seal_incomplete_phis_with(
|
||||
&mut ops_seal,
|
||||
cond_bb,
|
||||
bend,
|
||||
std::mem::take(&mut incomplete),
|
||||
&continue_snaps,
|
||||
)?;
|
||||
// 5) latch(body末尾)スナップショットを LoopForm 用のマップに登録
|
||||
ops.block_var_maps.insert(latch_bb, body_vars.clone());
|
||||
|
||||
// body から latch へのフォールスルー経路(continue / break は既に terminator 済み)
|
||||
if let Some(bb) = ops.f.get_block_mut(bend) {
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(ops.f, bend, latch_bb);
|
||||
}
|
||||
}
|
||||
// 6) exit PHIs(breakの合流 + headerからのフォールスルー)
|
||||
let mut ops_exit = Ops { f: ops.f, vars: ops.vars, block_var_maps: &block_var_maps2 };
|
||||
crate::mir::phi_core::loop_phi::build_exit_phis_with(
|
||||
&mut ops_exit,
|
||||
cond_bb,
|
||||
exit_bb,
|
||||
&header_vars_snapshot,
|
||||
&exit_snaps,
|
||||
)?;
|
||||
|
||||
// latch から header への canonical backedge
|
||||
crate::mir::ssot::cf_common::set_jump(ops.f, latch_bb, header_bb);
|
||||
|
||||
// 6) continue 経路を canonical continue_merge に統合し、header PHI 用 snapshot を 1 本にまとめる
|
||||
let canonical_continue_snaps: Vec<(BasicBlockId, HashMap<String, ValueId>)> =
|
||||
if continue_snaps.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
// 6-1) 各変数ごとに (pred_bb, value) の入力を集約
|
||||
let mut all_inputs: HashMap<String, Vec<(BasicBlockId, ValueId)>> = HashMap::new();
|
||||
for (bb, snap) in &continue_snaps {
|
||||
for (name, &val) in snap {
|
||||
all_inputs
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.push((*bb, val));
|
||||
}
|
||||
}
|
||||
|
||||
// 6-2) continue_merge_bb に必要な PHI を生成しつつ、merged_snapshot を作る
|
||||
let mut merged_snapshot: HashMap<String, ValueId> = HashMap::new();
|
||||
for (name, mut inputs) in all_inputs {
|
||||
let value = if let Some(same_val) =
|
||||
LoopSnapshotMergeBox::optimize_same_value(&inputs)
|
||||
{
|
||||
// 全て同じ値 or 単一入力 → PHI 不要
|
||||
same_val
|
||||
} else {
|
||||
// 異なる値を持つ場合は PHI ノードを continue_merge_bb に生成
|
||||
LoopSnapshotMergeBox::sanitize_inputs(&mut inputs);
|
||||
let phi_id = ops.f.next_value_id();
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(
|
||||
ops.f,
|
||||
continue_merge_bb,
|
||||
phi_id,
|
||||
inputs,
|
||||
);
|
||||
phi_id
|
||||
};
|
||||
merged_snapshot.insert(name, value);
|
||||
}
|
||||
|
||||
// continue_merge_bb から header への backedge を 1 本だけ張る
|
||||
crate::mir::ssot::cf_common::set_jump(ops.f, continue_merge_bb, header_bb);
|
||||
// LoopForm から get_variable_at_block されたときのために snapshot も登録
|
||||
ops.block_var_maps
|
||||
.insert(continue_merge_bb, merged_snapshot.clone());
|
||||
|
||||
vec![(continue_merge_bb, merged_snapshot)]
|
||||
};
|
||||
|
||||
// 7) header PHI seal(latch + canonical continue_merge スナップショット)
|
||||
loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps)?;
|
||||
|
||||
// 8) exit PHI(header fallthrough + break スナップショット)
|
||||
loopform.build_exit_phis(&mut ops, exit_bb, cend, &exit_snaps)?;
|
||||
|
||||
Ok(exit_bb)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user