srdatalog.ir.core.passes

Pass kinds, registration decorators, and driver.

A pass is a transformation on the IR. Two flavors:

Lowering — matches an op of one dialect, produces ops of another (or of a target dialect). Each Lowering carries the op class it matches, an apply callable that performs the transformation, declared consumes / produces dialect names for dependency validation, and a name for diagnostics.

Rewrite — matches an op, produces ops of the same dialect. Used for internal optimizations like the IIR-sorted-array count-as-product or hint-narrowing rules.

Per the project memory note (feedback_decorator_registries.md), registration uses Triton-style decorators rather than imperative register_X(OpClass, fn) calls or class-based dispatch:

from srdatalog.ir.core import lowering, rewrite, verifier
from srdatalog.ir.dialects.relation.sorted_array import DIALECT

@lowering(DIALECT, mir.ExecutePipeline,
          consumes=('mir',), produces=('iir.cf', 'relation.sorted_array'))
def _lower_execute_pipeline(ep, ctx):
    ...

@rewrite(DIALECT, SaPrefCoop)
def _hint_introduction(op, ctx):
    ...

@verifier(DIALECT)
def _verify_sorted_array(prog):
    return []   # list of VerificationError

The decorators mutate dialect.lowerings / dialect.rewrites / dialect.verifier in place. Decoration is the only registration path — there is no imperative API exposed.

The PassDriver walks compiler.dialects to validate dependencies (every Lowering’s consumes must be in the registered dialect set; otherwise raise PassDependencyError). Actual op-level dispatch is left to callers for now (production code calls lowering functions directly); the registry exists so external consumers can introspect “who lowers what” and so future stages can add a tree-walking dispatcher.

See docs/ir_lowering_semantics.md, section 21.

Module Contents

Classes

Lowering

A lowering rule from one dialect to another.

PassDriver

Runs registered rewrites and lowerings.

Rewrite

A rewrite rule within a single dialect.

Functions

lowering

Decorator: wrap fn as a Lowering and register on dialect.lowerings.

rewrite

Decorator: wrap fn as a Rewrite and register on dialect.rewrites.

verifier

Decorator: register fn as the dialect’s verifier.

Data

API

class srdatalog.ir.core.passes.Lowering[source]

A lowering rule from one dialect to another.

Fields: matches — the Op subclass this rule matches (e.g. MirColumnJoin). apply — callable taking (op, context) and returning the replacement IR (single op or list of ops; type depends on the target dialect’s contract). name — short identifier for diagnostics and pass tracing. consumes — dialect names whose ops this lowering reads. Used by PassDriver to validate that every required dialect is registered before the lowering runs. produces — dialect names whose ops this lowering emits. Used by PassDriver for topological ordering of multi-pass pipelines (a pass that produces dialect D must run before any pass that consumes D).

apply: collections.abc.Callable[[Any, Any], Any]

None

consumes: tuple[str, ...]

‘field(…)’

matches: type

None

name: str = <Multiline-String>
produces: tuple[str, ...]

‘field(…)’

exception srdatalog.ir.core.passes.PassDependencyError(pass_name: str, missing_dialect: str, in_dialect: str)[source]

Bases: Exception

A registered pass declared consumes=(D, ...) but dialect D is not registered with the Compiler. Raised by PassDriver.run before any pass executes.

The recommended posture (per docs/stage3a_execution_plan.md §9) is loud failure over silent fallback: a pipeline opting out of a dialect’s passes does so by not registering those passes, not by not registering the dialect.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class srdatalog.ir.core.passes.PassDriver(compiler: srdatalog.ir.core.dialect.Compiler)[source]

Runs registered rewrites and lowerings.

Today the driver does dependency validation (catches “pass P consumes dialect D not enabled”) and verifier dispatch. Op-level dispatch (a tree walker that finds the registered Lowering for each op kind and applies it) lands when the first production consumer needs it; until then, callers invoke lowering functions directly and the registry serves as introspection metadata.

The driver does not know about specific dialects. New dialects participate by being registered; the driver consults the registry.

Initialization

run(prog: Any) Any[source]

Run all registered passes against prog. Returns the (possibly transformed) program.

Validates pass dependencies first; raises PassDependencyError on unmet consumes. Then runs verifiers and returns prog unchanged (op-level dispatch is caller-driven for now; see class docstring).

validate_dependencies() None[source]

Check every registered Lowering / Rewrite’s consumes against the registered dialect set. Raises PassDependencyError on the first unmet dependency.

verify_all(prog: Any) list[Any][source]

Invoke every registered dialect’s verifier on prog and aggregate the returned VerificationErrors. Returns [] if all verifiers pass.

class srdatalog.ir.core.passes.Rewrite[source]

A rewrite rule within a single dialect.

Same shape as Lowering, but conventionally produces ops of the same dialect as matches. consumes / produces typically equal the dialect’s own name; PassDriver still uses them for dependency validation if a rewrite reads ops from a sibling dialect.

apply: collections.abc.Callable[[Any, Any], Any]

None

consumes: tuple[str, ...]

‘field(…)’

matches: type

None

name: str = <Multiline-String>
produces: tuple[str, ...]

‘field(…)’

srdatalog.ir.core.passes.__all__

[‘Lowering’, ‘PassDependencyError’, ‘PassDriver’, ‘Rewrite’, ‘lowering’, ‘rewrite’, ‘verifier’]

srdatalog.ir.core.passes.lowering(dialect: srdatalog.ir.core.dialect.Dialect, matches: type, *, consumes: tuple[str, ...] = (), produces: tuple[str, ...] = (), name: str = '') collections.abc.Callable[[collections.abc.Callable[[Any, Any], Any]], collections.abc.Callable[[Any, Any], Any]][source]

Decorator: wrap fn as a Lowering and register on dialect.lowerings.

Usage:

@lowering(MY_DIALECT, mir.SomeOp,
          consumes=('mir',),
          produces=('iir.cf', 'relation.sorted_array'))
def _lower_some_op(op, ctx):
    return ...

Returns the original function (so other decorators can stack).

srdatalog.ir.core.passes.rewrite(dialect: srdatalog.ir.core.dialect.Dialect, matches: type, *, consumes: tuple[str, ...] = (), produces: tuple[str, ...] = (), name: str = '') collections.abc.Callable[[collections.abc.Callable[[Any, Any], Any]], collections.abc.Callable[[Any, Any], Any]][source]

Decorator: wrap fn as a Rewrite and register on dialect.rewrites.

Usage:

@rewrite(MY_DIALECT, SomeOp)
def _hint_introduction(op, ctx):
    return ...
srdatalog.ir.core.passes.verifier(dialect: srdatalog.ir.core.dialect.Dialect) collections.abc.Callable[[collections.abc.Callable[[Any], Any]], collections.abc.Callable[[Any], Any]][source]

Decorator: register fn as the dialect’s verifier.

Usage:

@verifier(MY_DIALECT)
def _verify(prog):
    return []   # list of VerificationError, [] = OK

Raises ValueError if the dialect already has a verifier registered.