//! JoinLoopTrace - Unified tracing for JoinIR loop lowering //! //! This module consolidates all debug output for JoinIR loop operations into a single //! interface, making tracing consistent and controllable through environment variables. //! //! # Environment Variables //! //! - `NYASH_TRACE_VARMAP=1`: Enable variable_map tracing (shows variable → ValueId mappings) //! - `NYASH_JOINIR_DEBUG=1`: Enable general JoinIR debug output (pattern routing, merge stats) //! - `NYASH_OPTION_C_DEBUG=1`: Enable PHI-related debug (Option C PHI generation) //! - `NYASH_JOINIR_MAINLINE_DEBUG=1`: Enable mainline routing debug (function name matching) //! - `NYASH_LOOPFORM_DEBUG=1`: Enable LoopForm debug (control flow structure) //! //! # Output Format //! //! All trace output uses prefixed tags for easy filtering: //! //! ```text //! [trace:pattern] route: Pattern3_WithIfPhi MATCHED //! [trace:varmap] pattern3_before_merge: i→r4, sum→r7 //! [trace:joinir] merge_start: 3 functions, 45 blocks //! [trace:phi] pattern3: PHI already exists, skipping //! [trace:merge] pattern3: starting JoinIR merge //! [trace:exit_phi] pattern3: sum r7→r15 //! [trace:debug] router: Current function name: 'main' //! ``` //! //! # Examples //! //! ```bash //! # Enable variable_map tracing only //! NYASH_TRACE_VARMAP=1 ./target/release/hakorune test.hako 2>&1 | grep "\[trace:" //! //! # Enable all JoinIR debug output //! NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako 2>&1 | grep "\[trace:" //! //! # Enable PHI tracing //! NYASH_OPTION_C_DEBUG=1 ./target/release/hakorune test.hako 2>&1 | grep "\[trace:phi\]" //! //! # Enable multiple trace categories //! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako //! ``` use crate::mir::ValueId; use std::collections::BTreeMap; /// Unified tracer for JoinIR loop operations. /// /// Consolidates all debug output through a single interface, reading environment /// variables to control which trace categories are enabled. pub struct JoinLoopTrace { /// Whether varmap tracing is enabled (NYASH_TRACE_VARMAP) varmap_enabled: bool, /// Whether general JoinIR debug is enabled (NYASH_JOINIR_DEBUG) joinir_enabled: bool, /// Whether PHI debug is enabled (NYASH_OPTION_C_DEBUG) phi_enabled: bool, /// Whether mainline routing debug is enabled (NYASH_JOINIR_MAINLINE_DEBUG) mainline_enabled: bool, /// Whether LoopForm debug is enabled (NYASH_LOOPFORM_DEBUG) loopform_enabled: bool, } impl JoinLoopTrace { /// Create a new tracer, reading environment variables. pub fn new() -> Self { Self { varmap_enabled: std::env::var("NYASH_TRACE_VARMAP").is_ok(), joinir_enabled: std::env::var("NYASH_JOINIR_DEBUG").is_ok(), phi_enabled: std::env::var("NYASH_OPTION_C_DEBUG").is_ok(), mainline_enabled: std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok(), loopform_enabled: std::env::var("NYASH_LOOPFORM_DEBUG").is_ok(), } } /// Check if any tracing is enabled pub fn is_enabled(&self) -> bool { self.varmap_enabled || self.joinir_enabled || self.phi_enabled || self.mainline_enabled || self.loopform_enabled } /// Check if varmap tracing is enabled #[allow(dead_code)] pub fn is_varmap_enabled(&self) -> bool { self.varmap_enabled } /// Check if general joinir debug is enabled pub fn is_joinir_enabled(&self) -> bool { self.joinir_enabled } /// Check if mainline routing debug is enabled pub fn is_mainline_enabled(&self) -> bool { self.mainline_enabled } /// Check if loopform debug is enabled (legacy compatibility) pub fn is_loopform_enabled(&self) -> bool { self.loopform_enabled } /// Trace pattern detection/selection /// /// # Arguments /// - `tag`: Context identifier (e.g., "route", "pattern3") /// - `pattern_name`: Name of the pattern (e.g., "Pattern3_WithIfPhi") /// - `matched`: Whether the pattern matched (true) or was skipped (false) pub fn pattern(&self, tag: &str, pattern_name: &str, matched: bool) { if self.joinir_enabled || self.varmap_enabled { let status = if matched { "MATCHED" } else { "skipped" }; eprintln!("[trace:pattern] {}: {} {}", tag, pattern_name, status); } } /// Trace variable_map state /// /// # Arguments /// - `tag`: Context identifier (e.g., "pattern3_before_merge", "after_phi") /// - `vars`: The variable_map to display (variable name → ValueId) pub fn varmap(&self, tag: &str, vars: &BTreeMap) { if self.varmap_enabled { let entries: Vec = vars .iter() .map(|(k, v)| format!("{}→r{}", k, v.0)) .collect(); eprintln!("[trace:varmap] {}: {}", tag, entries.join(", ")); } } /// Trace JoinIR function/block counts /// /// # Arguments /// - `tag`: Context identifier (e.g., "merge_start", "after_allocation") /// - `func_count`: Number of functions in the JoinModule /// - `block_count`: Total number of blocks across all functions pub fn joinir_stats(&self, tag: &str, func_count: usize, block_count: usize) { if self.joinir_enabled { eprintln!( "[trace:joinir] {}: {} functions, {} blocks", tag, func_count, block_count ); } } /// Trace PHI operations /// /// # Arguments /// - `tag`: Context identifier (e.g., "pattern3", "exit_block") /// - `msg`: Human-readable message about the PHI operation #[allow(dead_code)] pub fn phi(&self, tag: &str, msg: &str) { if self.phi_enabled { eprintln!("[trace:phi] {}: {}", tag, msg); } } /// Trace merge operations /// /// # Arguments /// - `tag`: Context identifier (e.g., "pattern3", "block_allocation") /// - `msg`: Human-readable message about the merge operation #[allow(dead_code)] pub fn merge(&self, tag: &str, msg: &str) { if self.joinir_enabled || self.varmap_enabled { eprintln!("[trace:merge] {}: {}", tag, msg); } } /// Trace exit PHI connection (variable_map update) /// /// # Arguments /// - `tag`: Context identifier (e.g., "pattern3", "exit_reconnect") /// - `var_name`: Name of the variable being reconnected /// - `old_id`: Old ValueId (before exit PHI) /// - `new_id`: New ValueId (after exit PHI) #[allow(dead_code)] pub fn exit_phi(&self, tag: &str, var_name: &str, old_id: ValueId, new_id: ValueId) { if self.varmap_enabled { eprintln!( "[trace:exit_phi] {}: {} r{}→r{}", tag, var_name, old_id.0, new_id.0 ); } } /// Generic debug message (only if any tracing enabled) /// /// # Arguments /// - `tag`: Context identifier (e.g., "router", "pattern1") /// - `msg`: Human-readable debug message pub fn debug(&self, tag: &str, msg: &str) { if self.is_enabled() { eprintln!("[trace:debug] {}: {}", tag, msg); } } /// Trace function routing decisions /// /// # Arguments /// - `tag`: Context identifier (e.g., "router", "mainline") /// - `func_name`: Name of the function being routed /// - `msg`: Human-readable message about the routing decision pub fn routing(&self, tag: &str, func_name: &str, msg: &str) { if self.joinir_enabled || self.mainline_enabled { eprintln!( "[trace:routing] {}: function '{}' - {}", tag, func_name, msg ); } } /// Trace block allocation and remapping /// /// # Arguments /// - `tag`: Context identifier (e.g., "allocator", "remap") /// - `msg`: Human-readable message about block operations pub fn blocks(&self, tag: &str, msg: &str) { if self.joinir_enabled { eprintln!("[trace:blocks] {}: {}", tag, msg); } } /// Trace instruction rewriting /// /// # Arguments /// - `tag`: Context identifier (e.g., "rewriter", "phi_inject") /// - `msg`: Human-readable message about instruction operations pub fn instructions(&self, tag: &str, msg: &str) { if self.joinir_enabled { eprintln!("[trace:instructions] {}: {}", tag, msg); } } } impl Default for JoinLoopTrace { fn default() -> Self { Self::new() } } /// Global singleton for easy access (lazy initialized) /// /// This provides a convenient way to access the tracer without passing it around: /// /// ```rust /// trace::trace().varmap("my_tag", &variable_map); /// trace::trace().pattern("route", "Pattern1_Minimal", true); /// ``` pub fn trace() -> &'static JoinLoopTrace { use std::sync::OnceLock; static TRACE: OnceLock = OnceLock::new(); TRACE.get_or_init(JoinLoopTrace::new) }