217 lines
7.1 KiB
Rust
217 lines
7.1 KiB
Rust
/*!
|
||
* VM Loop/Phi Utilities
|
||
*
|
||
* Purpose: Track loop transitions and assist phi node resolution
|
||
* Responsibilities: PhiHandler/LoopExecutorでprevious_block・ループイテレーションを管理
|
||
* Key APIs: LoopExecutor::{new,record_transition,execute_phi}
|
||
* Typical Callers: VM 実行ループ(ブロック遷移/phi評価)
|
||
*/
|
||
|
||
use super::vm::{VMValue, VMError};
|
||
use crate::mir::{BasicBlockId, ValueId};
|
||
use std::collections::HashMap;
|
||
|
||
/// Phi nodeの実行ヘルパー
|
||
pub struct PhiHandler {
|
||
/// 現在のブロックに到達する前のブロック
|
||
previous_block: Option<BasicBlockId>,
|
||
|
||
/// Phi nodeの値キャッシュ(最適化用)
|
||
phi_cache: HashMap<ValueId, VMValue>,
|
||
}
|
||
|
||
impl PhiHandler {
|
||
/// 新しいPhiハンドラーを作成
|
||
pub fn new() -> Self {
|
||
Self {
|
||
previous_block: None,
|
||
phi_cache: HashMap::new(),
|
||
}
|
||
}
|
||
|
||
/// ブロック遷移を記録
|
||
pub fn record_block_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
|
||
self.previous_block = Some(from);
|
||
// ブロック遷移時にキャッシュをクリア(新しいイテレーション)
|
||
if self.is_loop_header(to) {
|
||
self.phi_cache.clear();
|
||
}
|
||
}
|
||
|
||
/// 初期ブロックへのエントリを記録
|
||
pub fn record_entry(&mut self) {
|
||
self.previous_block = None;
|
||
self.phi_cache.clear();
|
||
}
|
||
|
||
/// Phi命令を実行
|
||
pub fn execute_phi(
|
||
&mut self,
|
||
_dst: ValueId,
|
||
inputs: &[(BasicBlockId, ValueId)],
|
||
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
|
||
) -> Result<VMValue, VMError> {
|
||
// キャッシュは使わない - Phi nodeは毎回新しい値を計算する必要がある
|
||
// if let Some(cached) = self.phi_cache.get(&dst) {
|
||
// return Ok(cached.clone());
|
||
// }
|
||
|
||
// Phi nodeの入力を選択
|
||
let selected_value = self.select_phi_input(inputs, get_value_fn)?;
|
||
|
||
// キャッシュに保存(デバッグ用に残すが使わない)
|
||
// self.phi_cache.insert(dst, selected_value.clone());
|
||
|
||
Ok(selected_value)
|
||
}
|
||
|
||
/// Phi nodeの適切な入力を選択
|
||
fn select_phi_input(
|
||
&self,
|
||
inputs: &[(BasicBlockId, ValueId)],
|
||
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
|
||
) -> Result<VMValue, VMError> {
|
||
if inputs.is_empty() {
|
||
return Err(VMError::InvalidInstruction("Phi node has no inputs".to_string()));
|
||
}
|
||
|
||
// previous_blockに基づいて入力を選択
|
||
if let Some(prev_block) = self.previous_block {
|
||
// 対応するブロックからの入力を探す
|
||
for (block_id, value_id) in inputs {
|
||
if *block_id == prev_block {
|
||
let value = get_value_fn(*value_id)?;
|
||
return Ok(value);
|
||
}
|
||
}
|
||
|
||
// フォールバック:見つからない場合は最初の入力を使用
|
||
// これは通常起こらないはずだが、安全のため
|
||
}
|
||
|
||
// previous_blockがない場合(エントリポイント)は最初の入力を使用
|
||
let (_, value_id) = &inputs[0];
|
||
get_value_fn(*value_id)
|
||
}
|
||
|
||
/// ループヘッダーかどうかを判定(簡易版)
|
||
fn is_loop_header(&self, _block_id: BasicBlockId) -> bool {
|
||
// TODO: MIR情報からループヘッダーを判定する機能を追加
|
||
// 現在は常にfalse(キャッシュクリアしない)
|
||
false
|
||
}
|
||
}
|
||
|
||
/// ループ実行ヘルパー - ループ特有の処理を管理
|
||
pub struct LoopExecutor {
|
||
/// Phiハンドラー
|
||
phi_handler: PhiHandler,
|
||
|
||
/// ループイテレーション数(デバッグ用)
|
||
iteration_count: HashMap<BasicBlockId, usize>,
|
||
}
|
||
|
||
impl LoopExecutor {
|
||
/// 新しいループ実行ヘルパーを作成
|
||
pub fn new() -> Self {
|
||
Self {
|
||
phi_handler: PhiHandler::new(),
|
||
iteration_count: HashMap::new(),
|
||
}
|
||
}
|
||
|
||
/// ブロック遷移を記録
|
||
pub fn record_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
|
||
self.phi_handler.record_block_transition(from, to);
|
||
|
||
// ループイテレーション数を更新(デバッグ用)
|
||
if from > to { // 単純なバックエッジ検出
|
||
*self.iteration_count.entry(to).or_insert(0) += 1;
|
||
}
|
||
}
|
||
|
||
/// エントリポイントでの初期化
|
||
pub fn initialize(&mut self) {
|
||
self.phi_handler.record_entry();
|
||
self.iteration_count.clear();
|
||
}
|
||
|
||
/// Phi命令を実行
|
||
pub fn execute_phi(
|
||
&mut self,
|
||
dst: ValueId,
|
||
inputs: &[(BasicBlockId, ValueId)],
|
||
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
|
||
) -> Result<VMValue, VMError> {
|
||
self.phi_handler.execute_phi(dst, inputs, get_value_fn)
|
||
}
|
||
|
||
/// デバッグ情報を取得
|
||
pub fn debug_info(&self) -> String {
|
||
let mut info = String::new();
|
||
info.push_str("Loop Executor Debug Info:\n");
|
||
|
||
if let Some(prev) = self.phi_handler.previous_block {
|
||
info.push_str(&format!(" Previous block: {:?}\n", prev));
|
||
} else {
|
||
info.push_str(" Previous block: None (entry)\n");
|
||
}
|
||
|
||
if !self.iteration_count.is_empty() {
|
||
info.push_str(" Loop iterations:\n");
|
||
for (block, count) in &self.iteration_count {
|
||
info.push_str(&format!(" Block {:?}: {} iterations\n", block, count));
|
||
}
|
||
}
|
||
|
||
info
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_phi_selection() {
|
||
let mut handler = PhiHandler::new();
|
||
|
||
// テスト用の値
|
||
let inputs = vec![
|
||
(BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値
|
||
(BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値
|
||
];
|
||
|
||
// エントリポイントからの実行
|
||
handler.record_entry();
|
||
let result = handler.execute_phi(
|
||
ValueId::new(3),
|
||
&inputs,
|
||
|id| {
|
||
if id == ValueId::new(1) {
|
||
Ok(VMValue::Integer(0))
|
||
} else {
|
||
Ok(VMValue::Integer(10))
|
||
}
|
||
}
|
||
);
|
||
assert_eq!(result.unwrap(), VMValue::Integer(0));
|
||
|
||
// ループボディからの実行
|
||
handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1));
|
||
handler.phi_cache.clear(); // テスト用にキャッシュクリア
|
||
let result = handler.execute_phi(
|
||
ValueId::new(3),
|
||
&inputs,
|
||
|id| {
|
||
if id == ValueId::new(1) {
|
||
Ok(VMValue::Integer(0))
|
||
} else {
|
||
Ok(VMValue::Integer(10))
|
||
}
|
||
}
|
||
);
|
||
assert_eq!(result.unwrap(), VMValue::Integer(10));
|
||
}
|
||
}
|