HashMap iteration order is non-deterministic due to HashDoS protection. This caused merge_joinir_mir_blocks to sometimes process functions in wrong order, leading to incorrect block remapping. Changed: - MirModule.functions: HashMap → BTreeMap - MirInterpreter.functions: HashMap → BTreeMap - IfLoweringDryRunner.scan_module: HashMap → BTreeMap parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
167 lines
5.8 KiB
Rust
167 lines
5.8 KiB
Rust
//! Phase 33-10.0: If lowering dry-run スキャナー(箱化版)
|
||
//!
|
||
//! ## 責務
|
||
//! - MIR モジュール内のすべての Branch ブロックをスキャン
|
||
//! - try_lower_if_to_joinir() でパターンマッチングを試行(MIR書き換えなし)
|
||
//! - パフォーマンス計測と統計情報の収集
|
||
//!
|
||
//! ## 非責務
|
||
//! - MIR の書き換え(Route B実装時に別モジュールで実施)
|
||
//! - Loop lowering(別のdispatch経路)
|
||
|
||
use crate::mir::join_ir::JoinInst;
|
||
use crate::mir::{MirFunction, MirInstruction};
|
||
use std::collections::BTreeMap;
|
||
use std::time::{Duration, Instant};
|
||
|
||
/// If lowering dry-run スキャナー
|
||
pub struct IfLoweringDryRunner {
|
||
debug_level: u8,
|
||
}
|
||
|
||
/// Dry-run スキャン結果の統計情報
|
||
#[derive(Debug, Clone)]
|
||
pub struct DryRunStats {
|
||
pub total_branches: usize,
|
||
pub lowered_count: usize,
|
||
pub select_count: usize,
|
||
pub ifmerge_count: usize,
|
||
pub scan_duration: Duration,
|
||
}
|
||
|
||
impl IfLoweringDryRunner {
|
||
/// 新しい dry-run スキャナーを作成
|
||
pub fn new(debug_level: u8) -> Self {
|
||
Self { debug_level }
|
||
}
|
||
|
||
/// MIR モジュール全体をスキャンして If lowering 成功率を計測
|
||
///
|
||
/// ## 実装方針(Phase 33-9.2)
|
||
/// - Loop専任関数はスキップ(is_loop_lowered_function())
|
||
/// - 各 Branch ブロックで try_lower_if_to_joinir() 試行
|
||
/// - パフォーマンス計測(マイクロ秒レベル)
|
||
/// - 統計情報収集(Select/IfMerge分類)
|
||
pub fn scan_module(&self, functions: &BTreeMap<String, MirFunction>) -> DryRunStats {
|
||
let mut total_branches = 0;
|
||
let mut lowered_count = 0;
|
||
let mut select_count = 0;
|
||
let mut ifmerge_count = 0;
|
||
let start_scan = Instant::now();
|
||
|
||
for (func_name, func) in functions {
|
||
// Phase 33-9.1: Loop専任関数をスキップ
|
||
if crate::mir::join_ir::lowering::is_loop_lowered_function(func_name) {
|
||
continue;
|
||
}
|
||
|
||
// 各Branchブロックに対してtry_lower_if_to_joinir()試行
|
||
for (block_id, block) in &func.blocks {
|
||
if matches!(block.terminator, Some(MirInstruction::Branch { .. })) {
|
||
total_branches += 1;
|
||
let start = Instant::now();
|
||
|
||
match crate::mir::join_ir::lowering::try_lower_if_to_joinir(
|
||
func,
|
||
*block_id,
|
||
self.debug_level >= 3,
|
||
None, // Phase 61-1: Pure If(dry-runは常にPure If)
|
||
) {
|
||
Some(join_inst) => {
|
||
lowered_count += 1;
|
||
let elapsed = start.elapsed();
|
||
|
||
let inst_type = match &join_inst {
|
||
JoinInst::Select { .. } => {
|
||
select_count += 1;
|
||
"Select"
|
||
}
|
||
JoinInst::IfMerge { .. } => {
|
||
ifmerge_count += 1;
|
||
"IfMerge"
|
||
}
|
||
_ => "Other",
|
||
};
|
||
|
||
if self.debug_level >= 1 {
|
||
eprintln!(
|
||
"[joinir/if_lowering] ✅ {} block {:?}: {} ({:.2}μs)",
|
||
func_name,
|
||
block_id,
|
||
inst_type,
|
||
elapsed.as_micros()
|
||
);
|
||
}
|
||
}
|
||
None => {
|
||
if self.debug_level >= 2 {
|
||
eprintln!(
|
||
"[joinir/if_lowering] ⏭️ {} block {:?}: pattern not matched",
|
||
func_name, block_id
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
let scan_duration = start_scan.elapsed();
|
||
|
||
DryRunStats {
|
||
total_branches,
|
||
lowered_count,
|
||
select_count,
|
||
ifmerge_count,
|
||
scan_duration,
|
||
}
|
||
}
|
||
|
||
/// 統計情報を標準エラー出力に表示
|
||
pub fn print_stats(&self, stats: &DryRunStats) {
|
||
if self.debug_level >= 1 && stats.total_branches > 0 {
|
||
eprintln!("[joinir/if_lowering] 📊 Scan complete:");
|
||
eprintln!(" Total branches: {}", stats.total_branches);
|
||
eprintln!(
|
||
" Lowered: {} ({:.1}%)",
|
||
stats.lowered_count,
|
||
(stats.lowered_count as f64 / stats.total_branches as f64) * 100.0
|
||
);
|
||
eprintln!(" - Select: {}", stats.select_count);
|
||
eprintln!(" - IfMerge: {}", stats.ifmerge_count);
|
||
eprintln!(
|
||
" Scan time: {:.2}ms",
|
||
stats.scan_duration.as_secs_f64() * 1000.0
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_dry_runner_creation() {
|
||
let runner = IfLoweringDryRunner::new(0);
|
||
assert_eq!(runner.debug_level, 0);
|
||
|
||
let runner_verbose = IfLoweringDryRunner::new(3);
|
||
assert_eq!(runner_verbose.debug_level, 3);
|
||
}
|
||
|
||
#[test]
|
||
fn test_dry_run_stats_default() {
|
||
let stats = DryRunStats {
|
||
total_branches: 0,
|
||
lowered_count: 0,
|
||
select_count: 0,
|
||
ifmerge_count: 0,
|
||
scan_duration: Duration::from_millis(10),
|
||
};
|
||
|
||
assert_eq!(stats.total_branches, 0);
|
||
assert_eq!(stats.lowered_count, 0);
|
||
}
|
||
}
|