//! SSA IR, lowering, and optimization passes. //! //! The pipeline converts a CFG into a pruned SSA body consumed by the taint //! analysis engine. [`lower_to_ssa`] inserts phi nodes via Cytron's algorithm //! and renames variables along the dominator tree. [`optimize_ssa`] runs //! constant propagation, branch pruning, copy propagation, DCE, and type //! fact analysis in sequence. //! //! Key submodules: //! - [`ir`]: core types (`SsaValue`, `SsaOp`, `SsaInst`, `SsaBlock`, `SsaBody`) //! - [`lower`]: CFG-to-SSA lowering with Cytron phi insertion and dominator-tree rename //! - [`const_prop`]: sparse conditional constant propagation with branch pruning //! - [`copy_prop`]: copy and alias propagation //! - [`dce`]: dead definition elimination //! - [`type_facts`]: per-value type inference (`TypeKind`, `TypeFactResult`) //! - [`heap`]: abstract heap for container element abstractions //! - [`alias`]: base-variable alias groups from copy propagation #[allow(dead_code)] // IR types, fields used by Display impl, tests, and downstream analyses pub mod alias; pub mod const_prop; pub mod copy_prop; pub mod dce; pub mod display; pub mod heap; pub mod invariants; #[allow(dead_code)] pub mod ir; pub mod lower; pub mod param_points_to; pub mod pointsto; pub mod static_map; pub mod type_facts; #[allow(unused_imports)] pub use ir::*; pub use lower::lower_to_ssa; pub use lower::lower_to_ssa_scoped_nop; pub use lower::lower_to_ssa_with_params; use crate::cfg::Cfg; use crate::ssa::type_facts::TypeKind; use crate::symbol::Lang; use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Result of SSA optimization passes. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OptimizeResult { /// Per-SSA-value constant lattice values. pub const_values: HashMap, /// Type fact analysis results. pub type_facts: type_facts::TypeFactResult, /// Base-variable alias groups from copy propagation. pub alias_result: alias::BaseAliasResult, /// Points-to analysis: per-SSA-value abstract heap object sets. pub points_to: heap::PointsToResult, /// Module aliases from `require()` calls: SSA value → possible module names. /// Used to resolve dynamic dispatch like `lib.request()` where `lib = require("http")`. pub module_aliases: HashMap>, /// Number of branches pruned by constant propagation. pub branches_pruned: usize, /// Number of copies eliminated. pub copies_eliminated: usize, /// Number of dead definitions removed. pub dead_defs_removed: usize, } /// Run all SSA optimization passes on a body. /// /// Pipeline: const propagation → branch pruning → copy propagation → DCE → type facts. pub fn optimize_ssa(body: &mut SsaBody, cfg: &Cfg, lang: Option) -> OptimizeResult { optimize_ssa_with_param_types(body, cfg, lang, &[]) } /// Same as [`optimize_ssa`] but seeds [`SsaOp::Param`] values with /// per-position [`TypeKind`] facts derived from the function's /// `BodyMeta.param_types`. Strictly additive: an empty slice or /// `None` entries leave the type-fact analysis behaviour unchanged. pub fn optimize_ssa_with_param_types( body: &mut SsaBody, cfg: &Cfg, lang: Option, param_types: &[Option], ) -> OptimizeResult { // 1. Constant propagation (SCCP) let cp = const_prop::const_propagate(body); let branches_pruned = const_prop::apply_const_prop(body, &cp); // 2. Copy propagation let (copies_eliminated, copy_map) = copy_prop::copy_propagate(body, cfg); // 3. Alias analysis (uses copy_map before DCE removes dead defs) let alias_result = alias::compute_base_aliases(©_map, body); // 4. Dead code elimination let dead_defs_removed = dce::eliminate_dead_defs(body, cfg); // 5. Type fact analysis (uses const prop results + language for constructor inference) let type_facts = type_facts::analyze_types_with_param_types(body, cfg, &cp.values, lang, param_types); // 6. Points-to analysis (uses allocation site detection + SSA def-use) let points_to = heap::analyze_points_to(body, cfg, lang); // 7. Module alias analysis (require() tracking for JS/TS) let module_aliases = if matches!(lang, Some(Lang::JavaScript) | Some(Lang::TypeScript)) { const_prop::collect_module_aliases(body, &cp.values) } else { HashMap::new() }; OptimizeResult { const_values: cp.values, type_facts, alias_result, points_to, module_aliases, branches_pruned, copies_eliminated, dead_defs_removed, } }