20  Pass

A pass is a named compiler transformation or analysis scheduled by MLIR’s pass manager. It is not simply a function that walks operations. A pass declares its operation scope, required dialects, preserved analyses, and failure behavior so the pipeline can compose it safely with other passes.

20.1 Scope Is Part Of The Contract

A pass runs on a specific operation type. A module pass can inspect symbols and multiple functions; a function pass works on one function at a time; a loop pass can be nested under a function pipeline when its operation type permits it. This scope determines what state the pass may assume and how the pass manager can parallelize work.

A function pass must not quietly rely on mutable state shared across sibling functions. The pass manager may run those functions concurrently. Information that truly spans functions belongs in a module-level analysis or pass, with explicit synchronization and invalidation rules.

20.2 Transform, Analyze, Or Validate

A transformation pass changes IR. It must leave the target operation verified and should signal failure when it cannot preserve required semantics. An analysis pass computes information such as dominance, liveness, aliases, or call relationships. A validation pass may inspect IR and emit diagnostics without changing it.

These roles can be combined cautiously, but a pass that mutates IR invalidates facts computed before mutation unless it declares which analyses remain valid. Preserving an analysis you did not actually preserve produces subtle, order-dependent compiler bugs.

20.3 Dependencies And Registration

A pass declares dependent dialects when it constructs operations from them or needs their parsing/interfaces. It should also state required or generated analysis information through the pass manager APIs. A command-line pipeline only knows registered passes; an IR parser only knows registered dialects. These are different registrations and both matter.

Pass arguments are stable command-line identifiers. Keep them focused on the transformation’s semantic action and expose options for genuine policy choices, not temporary debugging state. A useful pass description tells a reader what IR it expects, what it produces, and the condition under which it fails.

20.4 A Safe Transformation Shape

A robust pass has a repeatable structure:

  1. Check preconditions and acquire analyses.
  2. Walk only the IR scope it is designed to understand.
  3. Use a rewriter for structural edits and preserve locations where possible.
  4. Verify or rely on the pass-manager verification boundary during development.
  5. Mark failure if it cannot meet its stated postcondition.

For example, a function-level constant-folding pass may replace pure operations with constants and erase now-dead operations. It must not erase a store merely because its result list is empty: the memory effect is observable.

20.5 Pipeline Position Matters

The same rewrite may be useful early and harmful late. A high-level tensor operation is valuable before fusion and shape analysis; lowering it to memory operations too early can erase the structure those passes need. Conversely, target lowering must eventually remove operations that the target does not understand. A pass should document the dialects and invariants it consumes and produces so the pipeline can place it correctly.