//! Phase 27.10: Common utilities for JoinIR lowering //! //! CFG sanity checks and dispatcher helpers for MIR-based lowering. use crate::mir::{BasicBlockId, BinaryOp, ConstValue, MirInstruction}; use crate::mir::query::{MirQuery, MirQueryBox}; /// Check if entry block has at least one successor /// /// Returns `true` if the entry block has at least one successor, `false` otherwise. /// This is a basic sanity check to ensure the MIR CFG is well-formed. pub fn ensure_entry_has_succs(query: &MirQueryBox, entry: BasicBlockId) -> bool { !query.succs(entry).is_empty() } /// Check if a basic block contains `Const { value: Integer(value) }` /// /// Returns `true` if the block contains a constant integer instruction with the specified value. /// /// # Example /// ```ignore /// // Check if entry block contains Const(0) /// if has_const_int(&query, entry_id, 0) { /// // ... /// } /// ``` pub fn has_const_int(query: &MirQueryBox, bb: BasicBlockId, value: i64) -> bool { query.insts_in_block(bb).iter().any(|inst| { matches!( inst, MirInstruction::Const { value: ConstValue::Integer(v), .. } if *v == value ) }) } /// Check if a basic block contains `Const { value: String(value) }` /// /// Returns `true` if the block contains a constant string instruction with the specified value. /// /// # Example /// ```ignore /// // Check if entry block contains Const("") /// if has_const_string(&query, entry_id, "") { /// // ... /// } /// ``` pub fn has_const_string(query: &MirQueryBox, bb: BasicBlockId, value: &str) -> bool { query.insts_in_block(bb).iter().any(|inst| { matches!( inst, MirInstruction::Const { value: ConstValue::String(s), .. } if s == value ) }) } /// Check if a basic block contains `BoxCall { method: method_name }` /// /// Returns `true` if the block contains a BoxCall instruction with the specified method name. /// /// # Example /// ```ignore /// // Check if entry block contains String.length() /// if has_string_method(&query, entry_id, "length") { /// // ... /// } /// ``` pub fn has_string_method(query: &MirQueryBox, bb: BasicBlockId, method: &str) -> bool { query.insts_in_block(bb).iter().any(|inst| { matches!( inst, MirInstruction::BoxCall { method: m, .. } if m == method ) }) } /// Check if a basic block contains `BinOp { op: operation }` /// /// Returns `true` if the block contains a binary operation instruction with the specified operation. /// /// # Example /// ```ignore /// // Check if entry block contains BinOp(Add) /// if has_binop(&query, entry_id, BinaryOp::Add) { /// // ... /// } /// ``` pub fn has_binop(query: &MirQueryBox, bb: BasicBlockId, op: BinaryOp) -> bool { query.insts_in_block(bb).iter().any(|inst| { matches!( inst, MirInstruction::BinOp { op: o, .. } if *o == op ) }) } /// Log fallback to handwritten lowering with reason /// /// Prints a diagnostic message when MIR-based lowering falls back to handwritten version. /// /// # Example /// ```ignore /// log_fallback("skip_ws", "entry has no successors"); /// // Output: [joinir/skip_ws/mir] unexpected MIR shape: entry has no successors, falling back to handwritten /// ``` pub fn log_fallback(tag: &str, reason: &str) { eprintln!( "[joinir/{}/mir] unexpected MIR shape: {}, falling back to handwritten", tag, reason ); } /// Dispatch between MIR-based and handwritten lowering based on environment variable /// /// Checks `NYASH_JOINIR_LOWER_FROM_MIR` and dispatches to the appropriate lowering function. /// This consolidates the toggle pattern used across all JoinIR lowering implementations. /// /// # Arguments /// - `tag`: Identifier for logging (e.g., "skip_ws", "trim") /// - `module`: MIR module to lower /// - `mir_based`: Closure for MIR-based lowering (returns `Option`) /// - `handwritten`: Closure for handwritten lowering (returns `Option`) /// /// # Returns /// `Option` - Both implementations may return None on errors /// /// # Example /// ```ignore /// pub fn lower_skip_ws_to_joinir(module: &MirModule) -> Option { /// dispatch_lowering( /// "skip_ws", /// module, /// lower_skip_ws_from_mir, /// lower_skip_ws_handwritten, /// ) /// } /// ``` pub fn dispatch_lowering( tag: &str, module: &crate::mir::MirModule, mir_based: F1, handwritten: F2, ) -> Option where F1: FnOnce(&crate::mir::MirModule) -> Option, F2: FnOnce(&crate::mir::MirModule) -> Option, { use crate::mir::join_ir::env_flag_is_1; if env_flag_is_1("NYASH_JOINIR_LOWER_FROM_MIR") { eprintln!("[joinir/{}] Using MIR-based lowering (NYASH_JOINIR_LOWER_FROM_MIR=1)", tag); mir_based(module) } else { eprintln!("[joinir/{}] Using handwritten lowering (default)", tag); handwritten(module) } }