//! Phase 72: PHI Reserved Region Verifier //! //! This module observes and verifies that PHI dst ValueIds stay within //! the reserved region (0-99) as documented in join_value_space.rs. //! //! ## Goal //! //! According to the doc, PHI dst should be in the "PHI Reserved (0-99)" region. //! However, the actual PHI dst allocation comes from `builder.next_value_id()` //! in loop_header_phi_builder.rs, not from JoinValueSpace. //! //! This verifier collects PHI dst values during tests and checks if they //! fall within the expected reserved region. use crate::mir::ValueId; use std::collections::BTreeSet; use std::sync::Mutex; /// Global collector for PHI dst observations (debug-only) static PHI_DST_OBSERVATIONS: Mutex>> = Mutex::new(None); /// Enable PHI dst observation (call this before tests) #[cfg(debug_assertions)] pub fn enable_observation() { let mut obs = PHI_DST_OBSERVATIONS.lock().unwrap(); *obs = Some(BTreeSet::new()); } /// Observe a PHI dst ValueId #[cfg(debug_assertions)] pub fn observe_phi_dst(dst: ValueId) { if let Ok(mut obs) = PHI_DST_OBSERVATIONS.lock() { if let Some(ref mut set) = *obs { set.insert(dst.0); } } } /// Get observation results and reset #[cfg(debug_assertions)] pub fn get_observations() -> Vec { let mut obs = PHI_DST_OBSERVATIONS.lock().unwrap(); if let Some(ref mut set) = *obs { let result = set.iter().copied().collect(); set.clear(); result } else { vec![] } } /// Disable observation #[cfg(debug_assertions)] pub fn disable_observation() { let mut obs = PHI_DST_OBSERVATIONS.lock().unwrap(); *obs = None; } /// Analyze PHI dst distribution #[cfg(debug_assertions)] pub fn analyze_distribution(observations: &[u32]) -> PhiDistributionReport { use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MIN, PARAM_MIN, PHI_RESERVED_MAX}; let mut in_reserved = 0; let mut in_param = 0; let mut in_local = 0; let mut min_val = u32::MAX; let mut max_val = 0; for &dst in observations { min_val = min_val.min(dst); max_val = max_val.max(dst); if dst <= PHI_RESERVED_MAX { in_reserved += 1; } else if dst < PARAM_MIN { // Between PHI_RESERVED_MAX+1 and PARAM_MIN-1 (gap region) in_param += 1; // Count as "leaked into param region vicinity" } else if dst < LOCAL_MIN { in_param += 1; } else { in_local += 1; } } PhiDistributionReport { total: observations.len(), in_reserved, in_param, in_local, min_val: if observations.is_empty() { None } else { Some(min_val) }, max_val: if observations.is_empty() { None } else { Some(max_val) }, } } /// Report of PHI dst distribution #[derive(Debug, Clone)] pub struct PhiDistributionReport { pub total: usize, pub in_reserved: usize, pub in_param: usize, pub in_local: usize, pub min_val: Option, pub max_val: Option, } impl PhiDistributionReport { /// Check if all PHI dsts are within reserved region (0-99) pub fn is_all_reserved(&self) -> bool { self.total > 0 && self.in_reserved == self.total } /// Get human-readable summary pub fn summary(&self) -> String { format!( "PHI dst distribution: total={}, reserved(0-99)={}, param(100-999)={}, local(1000+)={}, range=[{}-{}]", self.total, self.in_reserved, self.in_param, self.in_local, self.min_val.map_or("N/A".to_string(), |v| v.to_string()), self.max_val.map_or("N/A".to_string(), |v| v.to_string()) ) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_analyze_distribution_empty() { let report = analyze_distribution(&[]); assert_eq!(report.total, 0); assert_eq!(report.in_reserved, 0); assert!(report.min_val.is_none()); assert!(report.max_val.is_none()); } #[test] fn test_analyze_distribution_all_reserved() { let observations = vec![0, 5, 10, 50, 99]; let report = analyze_distribution(&observations); assert_eq!(report.total, 5); assert_eq!(report.in_reserved, 5); assert_eq!(report.in_param, 0); assert_eq!(report.in_local, 0); assert!(report.is_all_reserved()); assert_eq!(report.min_val, Some(0)); assert_eq!(report.max_val, Some(99)); } #[test] fn test_analyze_distribution_mixed() { let observations = vec![0, 50, 100, 500, 1000, 2000]; let report = analyze_distribution(&observations); assert_eq!(report.total, 6); assert_eq!(report.in_reserved, 2); // 0, 50 assert_eq!(report.in_param, 2); // 100, 500 assert_eq!(report.in_local, 2); // 1000, 2000 assert!(!report.is_all_reserved()); assert_eq!(report.min_val, Some(0)); assert_eq!(report.max_val, Some(2000)); } #[test] fn test_analyze_distribution_all_local() { let observations = vec![1000, 1500, 2000]; let report = analyze_distribution(&observations); assert_eq!(report.total, 3); assert_eq!(report.in_reserved, 0); assert_eq!(report.in_param, 0); assert_eq!(report.in_local, 3); assert!(!report.is_all_reserved()); } } #[cfg(test)] mod observation_tests { use super::*; /// Phase 72-1: Observe PHI dst distribution in existing tests /// /// This test runs BEFORE strengthening the verifier. It collects actual /// PHI dst values and reports their distribution. /// /// Expected outcomes: /// - If all PHI dst in reserved (0-99) → proceed to strengthen verifier /// - If some PHI dst outside reserved → document why and skip verifier #[test] fn test_phase72_observe_phi_dst_distribution() { // Enable observation enable_observation(); // The observations will come from loop_header_phi_builder // when it allocates PHI dst during other tests running in parallel // For this initial test, we just verify the mechanism works let observations = get_observations(); let report = analyze_distribution(&observations); eprintln!("\n[Phase 72] PHI dst observation report (initial):"); eprintln!(" {}", report.summary()); eprintln!(" All in reserved region: {}", report.is_all_reserved()); // This test always passes - it's for observation only disable_observation(); } }