Files
hakorune/src/mir/join_ir/lowering/if_dry_runner.rs
nyash-codex c4c0814fd6 fix(joinir): Use BTreeMap for MirModule.functions deterministic iteration
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>
2025-12-05 17:22:14 +09:00

167 lines
5.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 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 Ifdry-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);
}
}