aver/ir/mir/optimize/mod.rs
1//! Phase 6 MIR optimization passes.
2//!
3//! Each pass is a pure function `MirProgram → MirProgram`, so the
4//! pipeline composes through plain function chaining
5//! (`branch_collapse(bool_match_to_if(const_fold(program)))`).
6//! The VM compiler wires them up in
7//! `vm::compiler::compile_program_with_mir_fallback`.
8//!
9//! ## Pass directory
10//!
11//! - [`const_fold`] (wave 5) — evaluate `BinOp` / `Neg` over
12//! literal operands at compile time.
13//! - [`dead_code`] (wave 6) — drop `Let { value: <pure>, body }`
14//! bindings that `body` never reads.
15//! - [`inline`] (wave 7) — replace nullary literal-only fn
16//! calls with the callee's literal.
17//! - [`algebraic`] (wave 8) — rewrite Int identity shapes
18//! (`x + 0`, `x * 1`, `Neg(Neg(x))`).
19//! - [`bool_match`] (wave 9) — lift two-arm `Bool` `Match`
20//! into `MirExpr::IfThenElse`.
21//! - [`branch_collapse`] (wave 10) — drop the dead branch of
22//! an `IfThenElse` whose `cond` folded to a literal `Bool`.
23//!
24//! ## Cascades
25//!
26//! Two passes that look orthogonal compose in interesting ways
27//! end-to-end:
28//!
29//! - `const_fold` → `algebraic` → `dead_code`: `let x = 1 + 0; 7`
30//! folds the value, the symbolic identity drops the BinOp,
31//! DCE drops the unused binding.
32//! - `const_fold` → `bool_match` → `branch_collapse`:
33//! `match (5 == 5) { true → A; false → B }` folds the
34//! subject to `true`, lifts the match to `if true { A }
35//! else { B }`, collapses to `A`.
36//!
37//! ## What this layer doesn't transform (deferred)
38//!
39//! - General inlining with param substitution.
40//! - String concatenation folding (would need intern-side
41//! allocation policy; the VM does it efficiently at runtime
42//! already).
43//! - Match arms (would require pattern → literal
44//! classification plumbing; defer until there's a real perf
45//! signal).
46//! - CSE / loop-invariant hoisting — future waves.
47
48pub mod algebraic;
49pub mod bare_i64;
50pub mod bare_i64_rewrite;
51pub mod bool_match;
52pub mod branch_collapse;
53pub mod const_fold;
54pub mod dead_code;
55pub mod inline;
56pub mod own_param;
57
58#[cfg(test)]
59pub(crate) mod test_helpers;
60
61pub use algebraic::algebraic_simplify;
62pub use bare_i64::{BareI64Facts, FnBareFacts, Repr, ValueFact};
63pub use bool_match::bool_match_to_if;
64pub use branch_collapse::branch_collapse;
65pub use const_fold::const_fold;
66pub use dead_code::dead_code;
67pub use inline::inline_nullary_literals;
68pub use own_param::own_param_refine;
69
70/// The canonical Core-MIR optimization pipeline: the passes applied
71/// in dependency order
72/// (`inline → const_fold → algebraic → bool_match → branch_collapse →
73/// dead_code → own_param`). Every backend that runs the optimizer calls
74/// this single entry, so the pass set and their order live in one place
75/// instead of being re-nested per call site. `lower_program` produces
76/// the input.
77///
78/// `own_param_refine` runs LAST: it reads the final slot↔`LocalId`
79/// mapping (after any inlining that mints fresh locals) so its
80/// interprocedural Vector/Map-param un-flagging keys off slots that
81/// won't shift under a later pass — avoiding the
82/// `aliased_slots`-vs-`LocalId` desync class.
83pub fn optimize(program: crate::ir::mir::MirProgram) -> crate::ir::mir::MirProgram {
84 own_param_refine(dead_code(branch_collapse(bool_match_to_if(
85 algebraic_simplify(const_fold(inline_nullary_literals(program))),
86 ))))
87}