feat(joinir): Phase 61-3 IfInLoopPhiEmitter箱実装
If-in-loop PHI生成を箱化モジュール化: ## 新規ファイル - if_in_loop_phi_emitter.rs: IfInLoopPhiEmitter 箱(~250行) - emit_header_phis(): VarLookup方式でPHI生成 - ユニットテスト2件: basic / same_value ## 変更ファイル - mod.rs: IfInLoopPhiEmitter モジュール追加・pub use - if_lowering.rs: Phase 61-3本番経路統合 - HAKO_JOINIR_IF_IN_LOOP_ENABLE=1 で IfInLoopPhiEmitter 使用 - JoinIRパターンマッチ成功時のみ動作 - if_phi_context.rs: unused imports削除 ## 箱理論 - IfInLoopPhiEmitter: PHI命令発行に専念(Thin Box) - VarLookup方式: snapshot lookup + pre_val fallback - CFG非依存: incoming値はsnapshotから直接取得 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -27,8 +27,7 @@
|
||||
//! - carrier_names の判定ユーティリティ提供
|
||||
//! - Phase 61-3: incoming値解決(CFG依存ロジック)
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// If-in-loop 用 PHI コンテキスト
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
310
src/mir/loop_builder/if_in_loop_phi_emitter.rs
Normal file
310
src/mir/loop_builder/if_in_loop_phi_emitter.rs
Normal file
@ -0,0 +1,310 @@
|
||||
//! Phase 61-3: If-in-loop PHI Emitter
|
||||
//!
|
||||
//! ループ内 if の PHI 生成を担当する「箱」。
|
||||
//! JoinIR + PhiSpec から直接 PHI 命令を発行する。
|
||||
//!
|
||||
//! ## 箱理論における位置づけ
|
||||
//!
|
||||
//! ```text
|
||||
//! ┌─────────────────────────────────────────────────────────────┐
|
||||
//! │ if_lowering.rs(オーケストレーター) │
|
||||
//! │ ↓ │
|
||||
//! │ ┌─────────────────────┐ ┌─────────────────────────────┐ │
|
||||
//! │ │ JoinIR Lowering │ │ IfInLoopPhiEmitter │ │
|
||||
//! │ │ (PhiSpec生成) │→ │ (PHI命令発行) │ │
|
||||
//! │ └─────────────────────┘ └─────────────────────────────┘ │
|
||||
//! └─────────────────────────────────────────────────────────────┘
|
||||
//! ```
|
||||
//!
|
||||
//! ## 責務
|
||||
//!
|
||||
//! - Header PHI 生成(ループイテレーション継続用)
|
||||
//! - VarLookup 方式による incoming 値解決
|
||||
//! - PhiBuilderOps trait 経由での PHI 発行
|
||||
//!
|
||||
//! ## 設計原則
|
||||
//!
|
||||
//! - **Thin Box**: ロジックは最小限、組み立てに専念
|
||||
//! - **CFG非依存**: incoming値は snapshot から直接取得
|
||||
//! - **決定性保証**: BTreeSet/BTreeMap のみ使用
|
||||
|
||||
use crate::mir::control_form::IfShape;
|
||||
use crate::mir::join_ir::lowering::if_phi_spec::PhiSpec;
|
||||
use crate::mir::phi_core::phi_builder_box::PhiBuilderOps;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
/// If-in-loop PHI Emitter
|
||||
///
|
||||
/// JoinIR経路で生成された PhiSpec を元に、
|
||||
/// 実際の PHI 命令を発行する。
|
||||
pub struct IfInLoopPhiEmitter;
|
||||
|
||||
impl IfInLoopPhiEmitter {
|
||||
/// Header PHI 生成
|
||||
///
|
||||
/// ループキャリア変数に対して、then/else 腕の値を統合する PHI を生成する。
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `phi_spec` - JoinIR から計算された PHI 仕様
|
||||
/// * `pre_if_var_map` - if 直前の変数マップ(VarLookup)
|
||||
/// * `then_snapshot` - then 腕終了時の変数スナップショット
|
||||
/// * `else_snapshot_opt` - else 腕終了時の変数スナップショット(ない場合は None)
|
||||
/// * `carrier_names` - ループキャリア変数名(片腕 PHI 対象)
|
||||
/// * `ops` - PHI 生成操作インターフェース
|
||||
/// * `if_shape` - If 構造情報
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// 生成した PHI の数
|
||||
pub fn emit_header_phis<O: PhiBuilderOps>(
|
||||
phi_spec: &PhiSpec,
|
||||
pre_if_var_map: &BTreeMap<String, ValueId>,
|
||||
then_snapshot: &BTreeMap<String, ValueId>,
|
||||
else_snapshot_opt: Option<&BTreeMap<String, ValueId>>,
|
||||
carrier_names: &BTreeSet<String>,
|
||||
ops: &mut O,
|
||||
if_shape: &IfShape,
|
||||
) -> Result<usize, String> {
|
||||
let mut phi_count = 0;
|
||||
let trace_on = std::env::var("HAKO_JOINIR_IF_IN_LOOP_TRACE").ok().as_deref() == Some("1");
|
||||
|
||||
if trace_on {
|
||||
eprintln!(
|
||||
"[Phase 61-3] IfInLoopPhiEmitter::emit_header_phis start"
|
||||
);
|
||||
eprintln!("[Phase 61-3] header_phis: {:?}", phi_spec.header_phis);
|
||||
eprintln!("[Phase 61-3] carrier_names: {:?}", carrier_names);
|
||||
}
|
||||
|
||||
// Header PHI 生成: carrier_names に含まれる変数のみ
|
||||
for var_name in &phi_spec.header_phis {
|
||||
if !carrier_names.contains(var_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// VarLookup: pre_if値を取得
|
||||
let pre_val = match pre_if_var_map.get(var_name) {
|
||||
Some(&v) => v,
|
||||
None => {
|
||||
if trace_on {
|
||||
eprintln!(
|
||||
"[Phase 61-3] ⚠️ var={} not found in pre_if_var_map, skipping",
|
||||
var_name
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Then値: snapshot から取得、なければ pre_val
|
||||
let then_val = then_snapshot
|
||||
.get(var_name)
|
||||
.copied()
|
||||
.unwrap_or(pre_val);
|
||||
|
||||
// Else値: snapshot から取得、なければ pre_val(片腕 PHI パターン)
|
||||
let else_val = else_snapshot_opt
|
||||
.and_then(|m| m.get(var_name).copied())
|
||||
.unwrap_or(pre_val);
|
||||
|
||||
// 値が同一なら PHI 不要
|
||||
if then_val == else_val {
|
||||
if trace_on {
|
||||
eprintln!(
|
||||
"[Phase 61-3] var={}: then_val == else_val ({:?}), no PHI needed",
|
||||
var_name, then_val
|
||||
);
|
||||
}
|
||||
ops.update_var(var_name.clone(), then_val);
|
||||
continue;
|
||||
}
|
||||
|
||||
// PHI 生成
|
||||
let phi_dst = ops.new_value();
|
||||
let mut inputs = Vec::new();
|
||||
|
||||
// Then 入力
|
||||
inputs.push((if_shape.then_block, then_val));
|
||||
|
||||
// Else 入力
|
||||
if let Some(else_bb) = if_shape.else_block {
|
||||
inputs.push((else_bb, else_val));
|
||||
}
|
||||
|
||||
if trace_on {
|
||||
eprintln!(
|
||||
"[Phase 61-3] var={}: emit PHI dst={:?} inputs={:?}",
|
||||
var_name, phi_dst, inputs
|
||||
);
|
||||
}
|
||||
|
||||
ops.emit_phi(if_shape.merge_block, phi_dst, inputs)?;
|
||||
ops.update_var(var_name.clone(), phi_dst);
|
||||
phi_count += 1;
|
||||
}
|
||||
|
||||
if trace_on {
|
||||
eprintln!(
|
||||
"[Phase 61-3] IfInLoopPhiEmitter::emit_header_phis done: {} PHIs",
|
||||
phi_count
|
||||
);
|
||||
}
|
||||
|
||||
Ok(phi_count)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::BasicBlockId;
|
||||
|
||||
/// モック PhiBuilderOps 実装
|
||||
struct MockOps {
|
||||
next_id: u32,
|
||||
emitted_phis: Vec<(BasicBlockId, ValueId, Vec<(BasicBlockId, ValueId)>)>,
|
||||
var_updates: Vec<(String, ValueId)>,
|
||||
}
|
||||
|
||||
impl MockOps {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
next_id: 1000,
|
||||
emitted_phis: Vec::new(),
|
||||
var_updates: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PhiBuilderOps for MockOps {
|
||||
fn new_value(&mut self) -> ValueId {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
ValueId(id)
|
||||
}
|
||||
|
||||
fn emit_phi(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
dst: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
self.emitted_phis.push((block, dst, inputs));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_var(&mut self, name: String, value: ValueId) {
|
||||
self.var_updates.push((name, value));
|
||||
}
|
||||
|
||||
fn get_block_predecessors(&self, _block: BasicBlockId) -> Vec<BasicBlockId> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn emit_void(&mut self) -> ValueId {
|
||||
self.new_value()
|
||||
}
|
||||
|
||||
fn set_current_block(&mut self, _block: BasicBlockId) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_exists(&self, _block: BasicBlockId) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emit_header_phis_basic() {
|
||||
// Setup
|
||||
let mut phi_spec = PhiSpec::new();
|
||||
phi_spec.header_phis.insert("x".to_string());
|
||||
|
||||
let mut pre_if = BTreeMap::new();
|
||||
pre_if.insert("x".to_string(), ValueId(1));
|
||||
|
||||
let mut then_snap = BTreeMap::new();
|
||||
then_snap.insert("x".to_string(), ValueId(2)); // then で変更
|
||||
|
||||
let else_snap = BTreeMap::new(); // else では変更なし
|
||||
|
||||
let mut carriers = BTreeSet::new();
|
||||
carriers.insert("x".to_string());
|
||||
|
||||
let if_shape = IfShape {
|
||||
cond_block: BasicBlockId(10),
|
||||
then_block: BasicBlockId(11),
|
||||
else_block: Some(BasicBlockId(12)),
|
||||
merge_block: BasicBlockId(13),
|
||||
};
|
||||
|
||||
let mut ops = MockOps::new();
|
||||
|
||||
// Execute
|
||||
let count = IfInLoopPhiEmitter::emit_header_phis(
|
||||
&phi_spec,
|
||||
&pre_if,
|
||||
&then_snap,
|
||||
Some(&else_snap),
|
||||
&carriers,
|
||||
&mut ops,
|
||||
&if_shape,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verify
|
||||
assert_eq!(count, 1);
|
||||
assert_eq!(ops.emitted_phis.len(), 1);
|
||||
|
||||
let (block, dst, inputs) = &ops.emitted_phis[0];
|
||||
assert_eq!(*block, BasicBlockId(13)); // merge_block
|
||||
assert_eq!(inputs.len(), 2);
|
||||
assert_eq!(inputs[0], (BasicBlockId(11), ValueId(2))); // then: 変更値
|
||||
assert_eq!(inputs[1], (BasicBlockId(12), ValueId(1))); // else: pre_val
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emit_header_phis_same_value() {
|
||||
// 両腕で同じ値の場合、PHI不要
|
||||
let mut phi_spec = PhiSpec::new();
|
||||
phi_spec.header_phis.insert("x".to_string());
|
||||
|
||||
let mut pre_if = BTreeMap::new();
|
||||
pre_if.insert("x".to_string(), ValueId(1));
|
||||
|
||||
let mut then_snap = BTreeMap::new();
|
||||
then_snap.insert("x".to_string(), ValueId(1)); // 変更なし
|
||||
|
||||
let else_snap = BTreeMap::new();
|
||||
|
||||
let mut carriers = BTreeSet::new();
|
||||
carriers.insert("x".to_string());
|
||||
|
||||
let if_shape = IfShape {
|
||||
cond_block: BasicBlockId(10),
|
||||
then_block: BasicBlockId(11),
|
||||
else_block: Some(BasicBlockId(12)),
|
||||
merge_block: BasicBlockId(13),
|
||||
};
|
||||
|
||||
let mut ops = MockOps::new();
|
||||
|
||||
let count = IfInLoopPhiEmitter::emit_header_phis(
|
||||
&phi_spec,
|
||||
&pre_if,
|
||||
&then_snap,
|
||||
Some(&else_snap),
|
||||
&carriers,
|
||||
&mut ops,
|
||||
&if_shape,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(count, 0); // PHI不要
|
||||
assert_eq!(ops.emitted_phis.len(), 0);
|
||||
assert_eq!(ops.var_updates.len(), 1);
|
||||
assert_eq!(ops.var_updates[0], ("x".to_string(), ValueId(1)));
|
||||
}
|
||||
}
|
||||
@ -228,6 +228,12 @@ impl<'a> LoopBuilder<'a> {
|
||||
join_inst
|
||||
);
|
||||
|
||||
// Phase 61-2.3: JoinInstからPhiSpecを計算
|
||||
let joinir_spec = crate::mir::join_ir::lowering::if_phi_spec::compute_phi_spec_from_joinir(
|
||||
&if_phi_context,
|
||||
&join_inst,
|
||||
);
|
||||
|
||||
// Phase 61-2: dry-runモードでPHI仕様を検証
|
||||
if crate::config::env::joinir_if_in_loop_dryrun_enabled() {
|
||||
eprintln!("[Phase 61-2] 🔍 dry-run mode enabled");
|
||||
@ -240,23 +246,18 @@ impl<'a> LoopBuilder<'a> {
|
||||
_ => "Other",
|
||||
}
|
||||
);
|
||||
|
||||
// Phase 61-2.3: JoinInstからPhiSpecを計算
|
||||
let joinir_spec = crate::mir::join_ir::lowering::if_phi_spec::compute_phi_spec_from_joinir(
|
||||
&if_phi_context,
|
||||
&join_inst,
|
||||
);
|
||||
eprintln!(
|
||||
"[Phase 61-2] JoinIR PhiSpec: header={}, exit={}",
|
||||
joinir_spec.header_count(),
|
||||
joinir_spec.exit_count()
|
||||
);
|
||||
|
||||
// A/B比較用に保存
|
||||
joinir_phi_spec_opt = Some(joinir_spec);
|
||||
}
|
||||
|
||||
false // Phase 61-2では検証のみ、本番切り替えはPhase 61-3
|
||||
// A/B比較用に保存
|
||||
joinir_phi_spec_opt = Some(joinir_spec);
|
||||
|
||||
// Phase 61-3: 本番経路有効かどうかを返す
|
||||
crate::config::env::joinir_if_in_loop_enable()
|
||||
}
|
||||
None => {
|
||||
if crate::config::env::joinir_if_in_loop_dryrun_enabled() {
|
||||
@ -278,6 +279,30 @@ impl<'a> LoopBuilder<'a> {
|
||||
// Phase 35-5: if_body_local_merge.rs削除、PhiBuilderBoxに吸収
|
||||
// 理由: 箱理論による責務分離(ループスコープ分析 vs if-merge専用処理)
|
||||
|
||||
// Phase 61-3: JoinIR本番経路(IfInLoopPhiEmitter)
|
||||
if joinir_success {
|
||||
if let Some(ref joinir_spec) = joinir_phi_spec_opt {
|
||||
// IfInLoopPhiEmitter を使用してPHI生成
|
||||
let else_snap_opt = else_var_map_end_opt.as_ref();
|
||||
let phi_count = super::IfInLoopPhiEmitter::emit_header_phis(
|
||||
joinir_spec,
|
||||
&pre_if_var_map,
|
||||
&then_var_map_end,
|
||||
else_snap_opt,
|
||||
&carrier_names,
|
||||
&mut ops,
|
||||
&if_shape,
|
||||
)?;
|
||||
|
||||
if crate::config::env::joinir_if_in_loop_dryrun_enabled() {
|
||||
eprintln!(
|
||||
"[Phase 61-3] ✅ IfInLoopPhiEmitter generated {} PHIs",
|
||||
phi_count
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// フォールバック: PhiBuilderBox経路(既存)
|
||||
if !joinir_success {
|
||||
// Phase 26-E: PhiBuilderBox SSOT統合(If PHI生成)
|
||||
|
||||
@ -28,11 +28,14 @@
|
||||
*/
|
||||
|
||||
mod control;
|
||||
mod if_in_loop_phi_emitter;
|
||||
mod if_lowering;
|
||||
mod loop_form;
|
||||
mod phi_ops;
|
||||
mod statements;
|
||||
|
||||
pub use if_in_loop_phi_emitter::IfInLoopPhiEmitter;
|
||||
|
||||
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
||||
use std::collections::BTreeMap; // Phase 25.1: 決定性確保
|
||||
|
||||
|
||||
Reference in New Issue
Block a user