Skip to main content

Crate opt_lang

Crate opt_lang 

Source
Expand description

§opt_lang

SSA optimizations for ir_lang 2.0: the IR-to-IR stage of the -lang family’s native compiler path, between the front end’s lowering and instruction selection. It makes a program smaller and faster without changing what it computes, traps with, raises, or writes to memory.

§Quick start

One call optimizes every function of a module:

use ir_lang::{Linkage, Module, Overflow, Signature, Type};

// fn f(x: i64) -> i64 { let a = 2 + 3; let b = x * a; let c = x * a; b + c }
let mut m = Module::new("demo");
let f = m.declare_function("f", &Signature::new(&[Type::I64], &[Type::I64]), Linkage::Export)?;
let mut b = m.build(f)?;
let x = b.param(0)?;
let (two, three) = (b.iconst(Type::I64, 2)?, b.iconst(Type::I64, 3)?);
let a = b.add(two, three, Overflow::Wrap)?;
let p = b.mul(x, a, Overflow::Wrap)?;
let q = b.mul(x, a, Overflow::Wrap)?;
let r = b.add(p, q, Overflow::Wrap)?;
b.ret(&[r])?;

let stats = opt_lang::optimize(&mut m)?;
assert_eq!((stats.insts_before(), stats.insts_after()), (6, 3));
let text = m.display_function(f).to_string();
assert!(text.contains("v1: i64 = const 5"), "{text}");
assert!(text.contains("v2: i64 = mul<overflow=wrap> v0, v1"), "{text}");
assert!(text.contains("v3: i64 = add<overflow=wrap> v2, v2"), "{text}");

§The passes

PassWhat it does
PassKind::SimplifyCfgfolds constant and trivial branches, removes unreachable blocks, threads jumps through empty blocks, merges straight-line blocks
PassKind::Sccpsparse conditional constant propagation over block parameters, switch, and check edges, folding with the exact OPS policies (OPS v2 pow and saturate included), also when only some operands are known
PassKind::CopyPropreplaces block parameters that always receive one value, and removes them
PassKind::Gvndominator-based value numbering of pure operations
PassKind::Licmhoists pure loop-invariant operations into preheaders
PassKind::Dceremoves dead instructions and dead block parameters, keeping every side effect

optimize runs DEFAULT_PIPELINE to a fixpoint. Optimizer chooses the passes, the number of sweeps, the budget, and validation; run_pass runs one pass on a Builder for callers with their own pipeline.

§Guarantees

  • Semantics are preserved: return values, traps and their codes, check errors and their codes, exceptions, and memory effects (stores, atomics, volatile accesses, calls, and their order) are unchanged. Constant folding follows specs/OPS.md exactly, and an operation whose policy would raise at run time is never folded into a value. Checked by differential tests that run random programs before and after optimization in the ir-lang reference interpreter.
  • The IR stays valid: every pass leaves the function valid under ir_lang::Module::validate, including the safepoint rules for GC references. The validator runs after every pass in debug builds and tests. A check becomes a plain instruction only when ir-lang’s placement rule (can_error) accepts it; an error edge that can be taken is never removed.
  • Statepoints are kept by construction: no pass moves a call or safepoint, edits a GC list, or makes a ref (or a value derived from one) a constant, numbered, or hoisted, so input whose statepoints came from Builder::insert_statepoints stays valid without running it again. The tests check that running it on optimized output adds nothing.
  • Bounded work: every pass is iterative (no recursion over the input) and draws from a Budget; a function whose budget runs out is left valid and partly optimized.

What is not done yet: no memory optimizations (loads are not value-numbered or forwarded, stores are not removed), no inlining, no SROA, no strength reduction beyond x ** 2 to x * x, and fma is not constant-folded. See dev/ROADMAP.md.

The IR is ir-lang =2.0.0-alpha.3; callers must use the same release.

§Features

  • std (default): the standard library; without it the crate is #![no_std] and needs only alloc. Optimization results are identical either way (folding implements IEEE rounding and square root itself).

Structs§

Budget
A budget of work steps that every pass draws from.
Optimizer
The configured optimizer (Tier 2): which passes, how many sweeps, what budget, and whether to validate between passes.
PassStats
How often one pass ran and how often it changed something.
Stats
The result of an optimization run: sizes before and after, iterations, and per-pass counts.

Enums§

OptError
Why optimization stopped.
PassKind
One optimization pass.

Constants§

DEFAULT_MAX_ITERATIONS
The default number of pipeline sweeps per function.
DEFAULT_PIPELINE
The default pipeline, run to a fixpoint: simplify the CFG, propagate constants, propagate copies, number values, hoist invariants, delete dead code, and simplify the CFG again.
DEFAULT_STEPS_PER_UNIT
The default budget per function, in steps per unit of size (instructions + blocks + values). Far above what the linear passes need; it only binds on inputs built to trigger the non-linear parts.

Functions§

optimize
Optimizes every function of module with the default pipeline (Tier 1).
run_pass
Runs one pass on the function being edited, for callers that build their own pipeline (Tier 3).