Skip to main content

PassManager

Struct PassManager 

Source
pub struct PassManager<T> { /* private fields */ }
Expand description

An ordered pipeline of passes over a unit of type T.

A PassManager holds passes in the order they were registered and runs them over a unit. It is the scheduler, and scheduling is its only job: it never reads or rewrites the unit itself — it only calls the passes, in order, and records what they report. Registering a pass with add is the plugin seam: any crate can contribute a transform by implementing Pass<T> and adding it here.

Two ways to run a pipeline:

  • run makes a single sweep — every pass once, in order. This is the common case.
  • run_to_fixpoint repeats the sweep until a full pass over the pipeline changes nothing, or an iteration bound is reached. Use it when passes feed one another and a transform can expose further work (constant folding exposing dead code, which exposes more folding).

The manager is single-threaded by design: a pipeline is an inherently ordered sequence of mutations, so it carries no atomic overhead. Dispatch to a pass is one indirect call amortised over the whole unit that pass rewrites.

§Examples

use pass_lang::{Outcome, Pass, PassError, PassManager};

struct Double;
impl Pass<i64> for Double {
    fn name(&self) -> &'static str { "double" }
    fn run(&mut self, u: &mut i64) -> Result<Outcome, PassError> {
        *u *= 2;
        Ok(Outcome::Changed)
    }
}

let mut pm = PassManager::new();
pm.add(Double).add(Double);

let mut unit = 3;
let report = pm.run(&mut unit).unwrap();

assert_eq!(unit, 12);            // 3 -> 6 -> 12
assert_eq!(report.runs().len(), 2);

Implementations§

Source§

impl<T> PassManager<T>

Source

pub fn new() -> Self

Create an empty pipeline.

§Examples
use pass_lang::PassManager;

let pm = PassManager::<i64>::new();
assert!(pm.is_empty());
Source

pub fn add(&mut self, pass: impl Pass<T> + 'static) -> &mut Self

Register a pass at the end of the pipeline.

Returns &mut Self so registrations can be chained. This is the plugin seam: the pass must be 'static, and it runs after every pass already registered.

§Examples
use pass_lang::{Outcome, Pass, PassError, PassManager};

struct Noop(&'static str);
impl Pass<i64> for Noop {
    fn name(&self) -> &'static str { self.0 }
    fn run(&mut self, _: &mut i64) -> Result<Outcome, PassError> { Ok(Outcome::Unchanged) }
}

let mut pm = PassManager::new();
pm.add(Noop("first")).add(Noop("second"));
assert_eq!(pm.len(), 2);
Source

pub fn len(&self) -> usize

The number of registered passes.

Source

pub fn is_empty(&self) -> bool

Whether the pipeline has no passes.

Source

pub fn run(&mut self, unit: &mut T) -> Result<Report, PassError>

Run every pass once, in registration order.

Each pass transforms unit in place; the returned Report lists every pass that ran with the Outcome it reported. If a pass returns a PassError, the pipeline stops at that pass — later passes do not run — and the error is returned with the failing pass’s name stamped in.

The report’s converged is true when the sweep changed nothing (the unit is already at a fixpoint for this pipeline), and iterations is always 1.

§Errors

Returns the PassError of the first pass that fails.

§Examples
use pass_lang::{Outcome, Pass, PassError, PassManager};

struct FailIfNegative;
impl Pass<i64> for FailIfNegative {
    fn name(&self) -> &'static str { "fail-if-negative" }
    fn run(&mut self, u: &mut i64) -> Result<Outcome, PassError> {
        if *u < 0 { return Err(PassError::new("unit went negative")); }
        Ok(Outcome::Unchanged)
    }
}

let mut pm = PassManager::new();
pm.add(FailIfNegative);

let mut unit = -1;
let err = pm.run(&mut unit).unwrap_err();
assert_eq!(err.pass(), "fail-if-negative");
assert_eq!(err.message(), "unit went negative");
Source

pub fn run_to_fixpoint( &mut self, unit: &mut T, max_iters: usize, ) -> Result<Report, PassError>

Repeat the pipeline until it settles, or until max_iters sweeps run.

Each sweep runs every pass once, in order. After a sweep in which no pass reported Changed, the unit is at a fixpoint and the loop stops with converged true. If max_iters sweeps run while the unit is still changing, the loop stops with converged false — the bound guarantees termination even if a pass oscillates. Passing max_iters == 0 performs no sweeps.

The Report accumulates every pass execution across every sweep, so its runs length is the sum over sweeps (a short final sweep on error aside).

§Errors

Returns the PassError of the first pass that fails, on whichever sweep it fails.

§Examples
use pass_lang::{Outcome, Pass, PassError, PassManager};

// Halve toward 1; idempotent once it reaches 1.
struct Halve;
impl Pass<i64> for Halve {
    fn name(&self) -> &'static str { "halve" }
    fn run(&mut self, u: &mut i64) -> Result<Outcome, PassError> {
        if *u <= 1 { return Ok(Outcome::Unchanged); }
        *u /= 2;
        Ok(Outcome::Changed)
    }
}

let mut pm = PassManager::new();
pm.add(Halve);

let mut unit = 16;
let report = pm.run_to_fixpoint(&mut unit, 32).unwrap();
assert_eq!(unit, 1);
assert!(report.converged());

Trait Implementations§

Source§

impl<T> Default for PassManager<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for PassManager<T>

§

impl<T> !Send for PassManager<T>

§

impl<T> !Sync for PassManager<T>

§

impl<T> !UnwindSafe for PassManager<T>

§

impl<T> Freeze for PassManager<T>

§

impl<T> Unpin for PassManager<T>

§

impl<T> UnsafeUnpin for PassManager<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.