Skip to content

Traits

traits

OpTraitInvT = TypeVar('OpTraitInvT', bound=OpTrait) module-attribute

OpTrait dataclass

A trait attached to an operation definition. Traits can be used to define operation invariants, additional semantic information, or to group operations that have similar properties. Note that traits are the merge of traits and interfaces in MLIR.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class OpTrait:
    """
    A trait attached to an operation definition.
    Traits can be used to define operation invariants, additional semantic information,
    or to group operations that have similar properties.
    Note that traits are the merge of traits and interfaces in MLIR.
    """

    def verify(self, op: Operation) -> None:
        """Check that the operation satisfies the trait requirements."""
        pass

__init__() -> None

verify(op: Operation) -> None

Check that the operation satisfies the trait requirements.

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    """Check that the operation satisfies the trait requirements."""
    pass

ConstantLike dataclass

Bases: OpTrait, ABC

Operation known to be constant-like.

To participate in constant folding and other generic mechanisms implement HasFolder or HasFolderInterface for your operation.

See external documentation.

Source code in xdsl/traits.py
class ConstantLike(OpTrait, abc.ABC):
    """
    Operation known to be constant-like.

    To participate in constant folding and other generic mechanisms implement
    `HasFolder` or `HasFolderInterface` for your operation.

    See external [documentation](https://mlir.llvm.org/doxygen/classmlir_1_1OpTrait_1_1ConstantLike.html).
    """

    @staticmethod
    def get_constant_value(ssa_value: SSAValue) -> Attribute | None:
        """
        If the value is the result of a `ConstantLike` operation that implements
        `HasFolderInterface`, return the attribute returned by `fold` corresponding to
        the value's index in the list of results.
        """
        from xdsl.ir import Attribute, OpResult

        if (
            isinstance(ssa_value, OpResult)
            and (op := ssa_value.owner)
            and op.has_trait(ConstantLike)
            and (t := op.get_trait(HasFolder)) is not None
            and (values := t.fold(op)) is not None
            and isinstance(value := values[ssa_value.index], Attribute)
        ):
            return value

get_constant_value(ssa_value: SSAValue) -> Attribute | None staticmethod

If the value is the result of a ConstantLike operation that implements HasFolderInterface, return the attribute returned by fold corresponding to the value's index in the list of results.

Source code in xdsl/traits.py
@staticmethod
def get_constant_value(ssa_value: SSAValue) -> Attribute | None:
    """
    If the value is the result of a `ConstantLike` operation that implements
    `HasFolderInterface`, return the attribute returned by `fold` corresponding to
    the value's index in the list of results.
    """
    from xdsl.ir import Attribute, OpResult

    if (
        isinstance(ssa_value, OpResult)
        and (op := ssa_value.owner)
        and op.has_trait(ConstantLike)
        and (t := op.get_trait(HasFolder)) is not None
        and (values := t.fold(op)) is not None
        and isinstance(value := values[ssa_value.index], Attribute)
    ):
        return value

HasFolder dataclass

Bases: OpTrait

Operation known to support folding.

Source code in xdsl/traits.py
class HasFolder(OpTrait):
    """
    Operation known to support folding.
    """

    @classmethod
    @abc.abstractmethod
    def fold(cls, op: Operation) -> Sequence[SSAValue | Attribute] | None:
        """
        Attempts to fold the operation. The fold method cannot modify the IR.
        Returns either an existing SSAValue or an Attribute for each result of the operation.
        When folding is unsuccessful, returns None.
        """
        raise NotImplementedError()

fold(op: Operation) -> Sequence[SSAValue | Attribute] | None abstractmethod classmethod

Attempts to fold the operation. The fold method cannot modify the IR. Returns either an existing SSAValue or an Attribute for each result of the operation. When folding is unsuccessful, returns None.

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def fold(cls, op: Operation) -> Sequence[SSAValue | Attribute] | None:
    """
    Attempts to fold the operation. The fold method cannot modify the IR.
    Returns either an existing SSAValue or an Attribute for each result of the operation.
    When folding is unsuccessful, returns None.
    """
    raise NotImplementedError()

HasParent dataclass

Bases: OpTrait

Constraint the operation to have a specific parent operation.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class HasParent(OpTrait):
    """Constraint the operation to have a specific parent operation."""

    op_types: tuple[type[Operation], ...]

    def __init__(self, head_param: type[Operation], *tail_params: type[Operation]):
        object.__setattr__(self, "op_types", (head_param, *tail_params))

    def verify(self, op: Operation) -> None:
        parent = op.parent_op()
        # Don't check parent when op is detached
        if parent is None:
            return
        if isinstance(parent, self.op_types):
            return
        if len(self.op_types) == 1:
            raise VerifyException(
                f"'{op.name}' expects parent op '{self.op_types[0].name}'"
            )
        names = ", ".join(f"'{p.name}'" for p in self.op_types)
        raise VerifyException(f"'{op.name}' expects parent op to be one of {names}")

op_types: tuple[type[Operation], ...] instance-attribute

__init__(head_param: type[Operation], *tail_params: type[Operation])

Source code in xdsl/traits.py
def __init__(self, head_param: type[Operation], *tail_params: type[Operation]):
    object.__setattr__(self, "op_types", (head_param, *tail_params))

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    parent = op.parent_op()
    # Don't check parent when op is detached
    if parent is None:
        return
    if isinstance(parent, self.op_types):
        return
    if len(self.op_types) == 1:
        raise VerifyException(
            f"'{op.name}' expects parent op '{self.op_types[0].name}'"
        )
    names = ", ".join(f"'{p.name}'" for p in self.op_types)
    raise VerifyException(f"'{op.name}' expects parent op to be one of {names}")

HasAncestor dataclass

Bases: OpTrait

Constraint the operation to have a specific operation as ancestor, i.e. transitive parent.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class HasAncestor(OpTrait):
    """
    Constraint the operation to have a specific operation as ancestor, i.e. transitive
    parent.
    """

    op_types: tuple[type[Operation], ...]

    def __init__(self, head_param: type[Operation], *tail_params: type[Operation]):
        object.__setattr__(self, "op_types", (head_param, *tail_params))

    def verify(self, op: Operation) -> None:
        if self.get_ancestor(op) is None:
            if len(self.op_types) == 1:
                raise VerifyException(
                    f"'{op.name}' expects ancestor op '{self.op_types[0].name}'"
                )
            names = ", ".join(f"'{p.name}'" for p in self.op_types)
            raise VerifyException(
                f"'{op.name}' expects ancestor op to be one of {names}"
            )

    def walk_ancestors(self, op: Operation) -> Iterator[Operation]:
        """Iterates over the ancestors of an operation, including the input"""
        curr = op
        yield curr
        while (curr := curr.parent_op()) is not None:
            yield curr

    def get_ancestor(self, op: Operation) -> Operation | None:
        ancestors = self.walk_ancestors(op)
        matching_ancestors = (a for a in ancestors if isinstance(a, self.op_types))
        return next(matching_ancestors, None)

op_types: tuple[type[Operation], ...] instance-attribute

__init__(head_param: type[Operation], *tail_params: type[Operation])

Source code in xdsl/traits.py
def __init__(self, head_param: type[Operation], *tail_params: type[Operation]):
    object.__setattr__(self, "op_types", (head_param, *tail_params))

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    if self.get_ancestor(op) is None:
        if len(self.op_types) == 1:
            raise VerifyException(
                f"'{op.name}' expects ancestor op '{self.op_types[0].name}'"
            )
        names = ", ".join(f"'{p.name}'" for p in self.op_types)
        raise VerifyException(
            f"'{op.name}' expects ancestor op to be one of {names}"
        )

walk_ancestors(op: Operation) -> Iterator[Operation]

Iterates over the ancestors of an operation, including the input

Source code in xdsl/traits.py
def walk_ancestors(self, op: Operation) -> Iterator[Operation]:
    """Iterates over the ancestors of an operation, including the input"""
    curr = op
    yield curr
    while (curr := curr.parent_op()) is not None:
        yield curr

get_ancestor(op: Operation) -> Operation | None

Source code in xdsl/traits.py
def get_ancestor(self, op: Operation) -> Operation | None:
    ancestors = self.walk_ancestors(op)
    matching_ancestors = (a for a in ancestors if isinstance(a, self.op_types))
    return next(matching_ancestors, None)

IsTerminator dataclass

Bases: OpTrait

This trait provides verification and functionality for operations that are known to be terminators.

See external documentation.

Source code in xdsl/traits.py
class IsTerminator(OpTrait):
    """
    This trait provides verification and functionality for operations that are
    known to be terminators.

    See external [documentation](https://mlir.llvm.org/docs/Traits/#terminator).
    """

    def verify(self, op: Operation) -> None:
        """Check that the operation satisfies the IsTerminator trait requirements."""
        if op.parent is not None and op.parent.last_op != op:
            raise VerifyException(
                f"'{op.name}' must be the last operation in its parent block"
            )

verify(op: Operation) -> None

Check that the operation satisfies the IsTerminator trait requirements.

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    """Check that the operation satisfies the IsTerminator trait requirements."""
    if op.parent is not None and op.parent.last_op != op:
        raise VerifyException(
            f"'{op.name}' must be the last operation in its parent block"
        )

ReturnLike dataclass

Bases: OpTrait

This trait indicates that a terminator operation is "return-like". This means that it exits its current region and forwards its operands as "exit" values to the parent region. Operations with this trait are not permitted to contain successors or produce results.

Source code in xdsl/traits.py
class ReturnLike(OpTrait):
    """
    This trait indicates that a terminator operation is "return-like". This
    means that it exits its current region and forwards its operands as "exit"
    values to the parent region. Operations with this trait are not permitted to
    contain successors or produce results.
    """

    def verify(self, op: Operation) -> None:
        if not op.has_trait(IsTerminator):
            raise VerifyException(f"{op.name} is not a terminator")
        if op.results:
            raise VerifyException(f"{op.name} does not have zero results")
        if op.successors:
            raise VerifyException(f"{op.name} does not have zero successors")

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    if not op.has_trait(IsTerminator):
        raise VerifyException(f"{op.name} is not a terminator")
    if op.results:
        raise VerifyException(f"{op.name} does not have zero results")
    if op.successors:
        raise VerifyException(f"{op.name} does not have zero successors")

NoTerminator dataclass

Bases: OpTrait

Allow an operation to have single block regions with no terminator.

See external documentation.

Source code in xdsl/traits.py
class NoTerminator(OpTrait):
    """
    Allow an operation to have single block regions with no terminator.

    See external [documentation](https://mlir.llvm.org/docs/Traits/#terminator).
    """

    def verify(self, op: Operation) -> None:
        for region in op.regions:
            if len(region.blocks) > 1:
                raise VerifyException(
                    f"'{op.name}' does not contain single-block regions"
                )

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    for region in op.regions:
        if len(region.blocks) > 1:
            raise VerifyException(
                f"'{op.name}' does not contain single-block regions"
            )

SingleBlockImplicitTerminator dataclass

Bases: OpTrait

Checks the existence of the specified terminator to an operation which has single-block regions. The conditions for the implicit creation of the terminator depend on the operation and occur during its creation using the ensure_terminator method.

This should be fully compatible with MLIR's Trait.

See external documentation.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class SingleBlockImplicitTerminator(OpTrait):
    """
    Checks the existence of the specified terminator to an operation which has
    single-block regions.
    The conditions for the implicit creation of the terminator depend on the operation
    and occur during its creation using the `ensure_terminator` method.

    This should be fully compatible with MLIR's Trait.

    See external [documentation](https://mlir.llvm.org/docs/Traits/#single-block-with-implicit-terminator).
    """

    op_type: type[Operation]

    def verify(self, op: Operation) -> None:
        for region in op.regions:
            if len(region.blocks) > 1:
                raise VerifyException(
                    f"'{op.name}' does not contain single-block regions"
                )
            for block in region.blocks:
                if (last_op := block.last_op) is None:
                    raise VerifyException(
                        f"'{op.name}' contains empty block instead of at least "
                        f"terminating with {self.op_type.name}"
                    )

                if not isinstance(last_op, self.op_type):
                    raise VerifyException(
                        f"'{op.name}' terminates with operation {last_op.name} "
                        f"instead of {self.op_type.name}"
                    )

op_type: type[Operation] instance-attribute

__init__(op_type: type[Operation]) -> None

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    for region in op.regions:
        if len(region.blocks) > 1:
            raise VerifyException(
                f"'{op.name}' does not contain single-block regions"
            )
        for block in region.blocks:
            if (last_op := block.last_op) is None:
                raise VerifyException(
                    f"'{op.name}' contains empty block instead of at least "
                    f"terminating with {self.op_type.name}"
                )

            if not isinstance(last_op, self.op_type):
                raise VerifyException(
                    f"'{op.name}' terminates with operation {last_op.name} "
                    f"instead of {self.op_type.name}"
                )

IsolatedFromAbove dataclass

Bases: OpTrait

Constrains the contained operations to use only values defined inside this operation.

This should be fully compatible with MLIR's Trait.

See external documentation.

Source code in xdsl/traits.py
class IsolatedFromAbove(OpTrait):
    """
    Constrains the contained operations to use only values defined inside this
    operation.

    This should be fully compatible with MLIR's Trait.

    See external [documentation](https://mlir.llvm.org/docs/Traits/#isolatedfromabove).
    """

    def verify(self, op: Operation) -> None:
        # Start by checking all the passed operation's regions
        regions: list[Region] = list(op.regions)

        # While regions are left to check
        while regions:
            # Pop the first one
            region = regions.pop()
            # Check every block of the region
            for block in region.blocks:
                # Check every operation of the block
                for child_op in block.ops:
                    # Check every operand of the operation
                    for operand in child_op.operands:
                        # The operand must not be defined out of the IsolatedFromAbove op.
                        # Since op.is_ancestor(op) evaluates to True, also check if the operand is
                        # owned by the op itself (not allowed).
                        if not op.is_ancestor(operand.owner) or operand.owner is op:
                            raise VerifyException(
                                "Operation using value defined out of its "
                                f"IsolatedFromAbove parent: {child_op}"
                            )
                    # Check nested regions too; unless the operation is IsolatedFromAbove
                    # too; in which case it will check itself.
                    if not child_op.has_trait(IsolatedFromAbove):
                        regions += child_op.regions

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    # Start by checking all the passed operation's regions
    regions: list[Region] = list(op.regions)

    # While regions are left to check
    while regions:
        # Pop the first one
        region = regions.pop()
        # Check every block of the region
        for block in region.blocks:
            # Check every operation of the block
            for child_op in block.ops:
                # Check every operand of the operation
                for operand in child_op.operands:
                    # The operand must not be defined out of the IsolatedFromAbove op.
                    # Since op.is_ancestor(op) evaluates to True, also check if the operand is
                    # owned by the op itself (not allowed).
                    if not op.is_ancestor(operand.owner) or operand.owner is op:
                        raise VerifyException(
                            "Operation using value defined out of its "
                            f"IsolatedFromAbove parent: {child_op}"
                        )
                # Check nested regions too; unless the operation is IsolatedFromAbove
                # too; in which case it will check itself.
                if not child_op.has_trait(IsolatedFromAbove):
                    regions += child_op.regions

SymbolUserOpInterface dataclass

Bases: OpTrait, ABC

Used to represent operations that reference Symbol operations. This provides the ability to perform safe and efficient verification of symbol uses, as well as additional functionality.

See external documentation.

Source code in xdsl/traits.py
class SymbolUserOpInterface(OpTrait, abc.ABC):
    """
    Used to represent operations that reference Symbol operations. This provides the
    ability to perform safe and efficient verification of symbol uses, as well as
    additional functionality.

    See external [documentation](https://mlir.llvm.org/docs/Interfaces/#symbolinterfaces).
    """

    @abc.abstractmethod
    def verify(self, op: Operation) -> None:
        """
        This method should be adapted to the requirements of specific symbol users per
        operation.

        It corresponds to the verifySymbolUses in upstream MLIR.
        """
        raise NotImplementedError()

verify(op: Operation) -> None abstractmethod

This method should be adapted to the requirements of specific symbol users per operation.

It corresponds to the verifySymbolUses in upstream MLIR.

Source code in xdsl/traits.py
@abc.abstractmethod
def verify(self, op: Operation) -> None:
    """
    This method should be adapted to the requirements of specific symbol users per
    operation.

    It corresponds to the verifySymbolUses in upstream MLIR.
    """
    raise NotImplementedError()

SymbolTable dataclass

Bases: OpTrait

SymbolTable operations are containers for Symbol operations. They offer lookup functionality for Symbols, and enforce unique symbols amongst its children.

A SymbolTable operation is constrained to have a single single-block region.

Source code in xdsl/traits.py
class SymbolTable(OpTrait):
    """
    SymbolTable operations are containers for Symbol operations. They offer lookup
    functionality for Symbols, and enforce unique symbols amongst its children.

    A SymbolTable operation is constrained to have a single single-block region.
    """

    def verify(self, op: Operation):
        # import builtin here to avoid circular import
        from xdsl.dialects.builtin import StringAttr

        if len(op.regions) != 1:
            raise VerifyException(
                "Operations with a 'SymbolTable' must have exactly one region"
            )
        if len(op.regions[0].blocks) != 1:
            raise VerifyException(
                "Operations with a 'SymbolTable' must have exactly one block"
            )
        block = op.regions[0].blocks[0]
        met_names: set[StringAttr] = set()
        for o in block.ops:
            if (sym_name := o.get_attr_or_prop("sym_name")) is None:
                continue
            if not isinstance(sym_name, StringAttr):
                continue
            if sym_name in met_names:
                raise VerifyException(f'Redefinition of symbol "{sym_name.data}"')
            met_names.add(sym_name)

    @staticmethod
    def lookup_symbol(
        op: Operation, name: str | StringAttr | SymbolRefAttr
    ) -> Operation | None:
        """
        Lookup a symbol by reference, starting from a specific operation's closest
        SymbolTable parent.
        """
        # import builtin here to avoid circular import
        from xdsl.dialects.builtin import StringAttr, SymbolRefAttr

        anchor: Operation | None = op
        while anchor is not None and not anchor.has_trait(SymbolTable):
            anchor = anchor.parent_op()
        if anchor is None:
            raise ValueError(f"Operation {op} has no SymbolTable ancestor")
        if isinstance(name, str | StringAttr):
            name = SymbolRefAttr(name)
        for o in anchor.regions[0].block.ops:
            if (
                sym_interface := o.get_trait(SymbolOpInterface)
            ) is not None and sym_interface.get_sym_attr_name(o) == name.root_reference:
                if not name.nested_references:
                    return o
                nested_root, *nested_references = name.nested_references.data
                nested_name = SymbolRefAttr(nested_root, nested_references)
                return SymbolTable.lookup_symbol(o, nested_name)
        return None

    @staticmethod
    def insert_or_update(
        symbol_table_op: Operation, symbol_op: Operation
    ) -> Operation | None:
        """
        This takes a symbol_table_op and a symbol_op. It looks if another operation
        inside symbol_table_op already defines symbol_ops symbol. If another operation
        is found, it replaces that operation with symbol_op. Otherwise, symbol_op is
        inserted at the end of symbol_table_op.

        This method returns the operation that was replaced or None if no operation
        was replaced.
        """
        trait = symbol_op.get_trait(SymbolOpInterface)

        if trait is None:
            raise ValueError(
                "Passed symbol_op does not have the SymbolOpInterface trait"
            )

        symbol_name = trait.get_sym_attr_name(symbol_op)

        if symbol_name is None:
            raise ValueError("Passed symbol_op does not have a symbol attribute name")

        tbl_trait = symbol_table_op.get_trait(SymbolTable)

        if tbl_trait is None:
            raise ValueError("Passed symbol_table_op does not have a SymbolTable trait")

        defined_symbol = tbl_trait.lookup_symbol(symbol_table_op, symbol_name)

        if defined_symbol is None:
            symbol_table_op.regions[0].blocks[0].add_op(symbol_op)
            return None
        else:
            parent = defined_symbol.parent
            assert parent is not None
            parent.insert_op_after(symbol_op, defined_symbol)
            parent.detach_op(defined_symbol)
            return defined_symbol

verify(op: Operation)

Source code in xdsl/traits.py
def verify(self, op: Operation):
    # import builtin here to avoid circular import
    from xdsl.dialects.builtin import StringAttr

    if len(op.regions) != 1:
        raise VerifyException(
            "Operations with a 'SymbolTable' must have exactly one region"
        )
    if len(op.regions[0].blocks) != 1:
        raise VerifyException(
            "Operations with a 'SymbolTable' must have exactly one block"
        )
    block = op.regions[0].blocks[0]
    met_names: set[StringAttr] = set()
    for o in block.ops:
        if (sym_name := o.get_attr_or_prop("sym_name")) is None:
            continue
        if not isinstance(sym_name, StringAttr):
            continue
        if sym_name in met_names:
            raise VerifyException(f'Redefinition of symbol "{sym_name.data}"')
        met_names.add(sym_name)

lookup_symbol(op: Operation, name: str | StringAttr | SymbolRefAttr) -> Operation | None staticmethod

Lookup a symbol by reference, starting from a specific operation's closest SymbolTable parent.

Source code in xdsl/traits.py
@staticmethod
def lookup_symbol(
    op: Operation, name: str | StringAttr | SymbolRefAttr
) -> Operation | None:
    """
    Lookup a symbol by reference, starting from a specific operation's closest
    SymbolTable parent.
    """
    # import builtin here to avoid circular import
    from xdsl.dialects.builtin import StringAttr, SymbolRefAttr

    anchor: Operation | None = op
    while anchor is not None and not anchor.has_trait(SymbolTable):
        anchor = anchor.parent_op()
    if anchor is None:
        raise ValueError(f"Operation {op} has no SymbolTable ancestor")
    if isinstance(name, str | StringAttr):
        name = SymbolRefAttr(name)
    for o in anchor.regions[0].block.ops:
        if (
            sym_interface := o.get_trait(SymbolOpInterface)
        ) is not None and sym_interface.get_sym_attr_name(o) == name.root_reference:
            if not name.nested_references:
                return o
            nested_root, *nested_references = name.nested_references.data
            nested_name = SymbolRefAttr(nested_root, nested_references)
            return SymbolTable.lookup_symbol(o, nested_name)
    return None

insert_or_update(symbol_table_op: Operation, symbol_op: Operation) -> Operation | None staticmethod

This takes a symbol_table_op and a symbol_op. It looks if another operation inside symbol_table_op already defines symbol_ops symbol. If another operation is found, it replaces that operation with symbol_op. Otherwise, symbol_op is inserted at the end of symbol_table_op.

This method returns the operation that was replaced or None if no operation was replaced.

Source code in xdsl/traits.py
@staticmethod
def insert_or_update(
    symbol_table_op: Operation, symbol_op: Operation
) -> Operation | None:
    """
    This takes a symbol_table_op and a symbol_op. It looks if another operation
    inside symbol_table_op already defines symbol_ops symbol. If another operation
    is found, it replaces that operation with symbol_op. Otherwise, symbol_op is
    inserted at the end of symbol_table_op.

    This method returns the operation that was replaced or None if no operation
    was replaced.
    """
    trait = symbol_op.get_trait(SymbolOpInterface)

    if trait is None:
        raise ValueError(
            "Passed symbol_op does not have the SymbolOpInterface trait"
        )

    symbol_name = trait.get_sym_attr_name(symbol_op)

    if symbol_name is None:
        raise ValueError("Passed symbol_op does not have a symbol attribute name")

    tbl_trait = symbol_table_op.get_trait(SymbolTable)

    if tbl_trait is None:
        raise ValueError("Passed symbol_table_op does not have a SymbolTable trait")

    defined_symbol = tbl_trait.lookup_symbol(symbol_table_op, symbol_name)

    if defined_symbol is None:
        symbol_table_op.regions[0].blocks[0].add_op(symbol_op)
        return None
    else:
        parent = defined_symbol.parent
        assert parent is not None
        parent.insert_op_after(symbol_op, defined_symbol)
        parent.detach_op(defined_symbol)
        return defined_symbol

SymbolOpInterface dataclass

Bases: OpTrait

A Symbol is a named operation that resides immediately within a region that defines a SymbolTable (TODO). A Symbol operation should use the SymbolOpInterface interface to provide the necessary verification and accessors.

A Symbol operation may be optional or not. If - the default - it is not optional, a sym_name attribute of type StringAttr is required. If it is optional, the attribute is optional too.

xDSL offers OptionalSymbolOpInterface as an always-optional SymbolOpInterface helper.

More requirements are defined in MLIR; Please see MLIR documentation for Symbol and SymbolTable for the requirements that are upcoming in xDSL.

See external documentation.

Source code in xdsl/traits.py
class SymbolOpInterface(OpTrait):
    """
    A `Symbol` is a named operation that resides immediately within a region that defines
    a `SymbolTable` (TODO). A Symbol operation should use the SymbolOpInterface interface to
    provide the necessary verification and accessors.

    A Symbol operation may be optional or not. If - the default - it is not optional,
    a `sym_name` attribute of type StringAttr is required. If it is optional,
    the attribute is optional too.

    xDSL offers OptionalSymbolOpInterface as an always-optional SymbolOpInterface helper.

    More requirements are defined in MLIR; Please see MLIR documentation for Symbol and
    SymbolTable for the requirements that are upcoming in xDSL.

    See external [documentation](https://mlir.llvm.org/docs/SymbolsAndSymbolTables/#symbol).
    """

    def get_sym_attr_name(self, op: Operation) -> StringAttr | None:
        """
        Returns the symbol of the operation, if any
        """
        # import builtin here to avoid circular import
        from xdsl.dialects.builtin import StringAttr

        sym_name = op.get_attr_or_prop("sym_name")
        if sym_name is None and self.is_optional_symbol(op):
            return None
        if not isinstance(sym_name, StringAttr):
            raise VerifyException(
                f'Operation {op.name} must have a "sym_name" attribute of type '
                f"`StringAttr` to conform to {SymbolOpInterface.__name__}"
            )
        return sym_name

    def is_optional_symbol(self, op: Operation) -> bool:
        """
        Returns true if this operation optionally defines a symbol based on the
        presence of the symbol name.
        """
        return False

    def verify(self, op: Operation) -> None:
        # This helper has the same behaviour, so we reuse it as a verifier.That is, it
        # raises a VerifyException iff this operation is a non-optional symbol *and*
        # there is no "sym_name" attribute or property.
        self.get_sym_attr_name(op)

get_sym_attr_name(op: Operation) -> StringAttr | None

Returns the symbol of the operation, if any

Source code in xdsl/traits.py
def get_sym_attr_name(self, op: Operation) -> StringAttr | None:
    """
    Returns the symbol of the operation, if any
    """
    # import builtin here to avoid circular import
    from xdsl.dialects.builtin import StringAttr

    sym_name = op.get_attr_or_prop("sym_name")
    if sym_name is None and self.is_optional_symbol(op):
        return None
    if not isinstance(sym_name, StringAttr):
        raise VerifyException(
            f'Operation {op.name} must have a "sym_name" attribute of type '
            f"`StringAttr` to conform to {SymbolOpInterface.__name__}"
        )
    return sym_name

is_optional_symbol(op: Operation) -> bool

Returns true if this operation optionally defines a symbol based on the presence of the symbol name.

Source code in xdsl/traits.py
def is_optional_symbol(self, op: Operation) -> bool:
    """
    Returns true if this operation optionally defines a symbol based on the
    presence of the symbol name.
    """
    return False

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    # This helper has the same behaviour, so we reuse it as a verifier.That is, it
    # raises a VerifyException iff this operation is a non-optional symbol *and*
    # there is no "sym_name" attribute or property.
    self.get_sym_attr_name(op)

OptionalSymbolOpInterface dataclass

Bases: SymbolOpInterface

Helper interface specialization for an optional SymbolOpInterface.

Source code in xdsl/traits.py
class OptionalSymbolOpInterface(SymbolOpInterface):
    """
    Helper interface specialization for an optional SymbolOpInterface.
    """

    def is_optional_symbol(self, op: Operation) -> bool:
        return True

is_optional_symbol(op: Operation) -> bool

Source code in xdsl/traits.py
def is_optional_symbol(self, op: Operation) -> bool:
    return True

CallableOpInterface dataclass

Bases: OpTrait, ABC

Interface for function-like Operations that can be called in a generic way.

Please see MLIR documentation for CallOpInterface and CallableOpInterface for more information.

See external documentation.

Source code in xdsl/traits.py
class CallableOpInterface(OpTrait, abc.ABC):
    """
    Interface for function-like Operations that can be called in a generic way.

    Please see MLIR documentation for CallOpInterface and CallableOpInterface for more
    information.

    See external [documentation](https://mlir.llvm.org/docs/Interfaces/#callinterfaces).
    """

    @classmethod
    @abc.abstractmethod
    def get_callable_region(cls, op: Operation) -> Region:
        """
        Returns the body of the operation
        """
        raise NotImplementedError()

    @classmethod
    @abc.abstractmethod
    def get_argument_types(cls, op: Operation) -> tuple[Attribute, ...]:
        raise NotImplementedError()

    @classmethod
    @abc.abstractmethod
    def get_result_types(cls, op: Operation) -> tuple[Attribute, ...]:
        raise NotImplementedError()

get_callable_region(op: Operation) -> Region abstractmethod classmethod

Returns the body of the operation

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_callable_region(cls, op: Operation) -> Region:
    """
    Returns the body of the operation
    """
    raise NotImplementedError()

get_argument_types(op: Operation) -> tuple[Attribute, ...] abstractmethod classmethod

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_argument_types(cls, op: Operation) -> tuple[Attribute, ...]:
    raise NotImplementedError()

get_result_types(op: Operation) -> tuple[Attribute, ...] abstractmethod classmethod

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_result_types(cls, op: Operation) -> tuple[Attribute, ...]:
    raise NotImplementedError()

HasCanonicalizationPatternsTrait dataclass

Bases: OpTrait

Provides the rewrite passes to canonicalize an operation.

Each rewrite pattern must have the trait's op as root.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class HasCanonicalizationPatternsTrait(OpTrait):
    """
    Provides the rewrite passes to canonicalize an operation.

    Each rewrite pattern must have the trait's op as root.
    """

    def get_patterns(
        self,
        op: type[Operation],
    ) -> tuple[RewritePattern, ...]:
        return type(self).get_canonicalization_patterns()

    @classmethod
    @abc.abstractmethod
    def get_canonicalization_patterns(cls) -> tuple[RewritePattern, ...]:
        raise NotImplementedError()

__init__() -> None

get_patterns(op: type[Operation]) -> tuple[RewritePattern, ...]

Source code in xdsl/traits.py
def get_patterns(
    self,
    op: type[Operation],
) -> tuple[RewritePattern, ...]:
    return type(self).get_canonicalization_patterns()

get_canonicalization_patterns() -> tuple[RewritePattern, ...] abstractmethod classmethod

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_canonicalization_patterns(cls) -> tuple[RewritePattern, ...]:
    raise NotImplementedError()

HasShapeInferencePatternsTrait dataclass

Bases: OpTrait

Provides the rewrite passes to shape infer an operation.

Each rewrite pattern must have the trait's op as root.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class HasShapeInferencePatternsTrait(OpTrait):
    """
    Provides the rewrite passes to shape infer an operation.

    Each rewrite pattern must have the trait's op as root.
    """

    def verify(self, op: Operation) -> None:
        return

    @classmethod
    @abc.abstractmethod
    def get_shape_inference_patterns(cls) -> tuple[RewritePattern, ...]:
        raise NotImplementedError()

__init__() -> None

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    return

get_shape_inference_patterns() -> tuple[RewritePattern, ...] abstractmethod classmethod

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_shape_inference_patterns(cls) -> tuple[RewritePattern, ...]:
    raise NotImplementedError()

MemoryEffectKind

Bases: Enum

The kind of side effect an operation can have.

MLIR has a more detailed version of this, able to tie effects to specific resources or values. Here, everything has its effect on the universe.

Source code in xdsl/traits.py
class MemoryEffectKind(Enum):
    """
    The kind of side effect an operation can have.

    MLIR has a more detailed version of this, able to tie effects to specific resources or
    values. Here, everything has its effect on the universe.
    """

    READ = auto()
    """
    Indicates that the operation reads from some resource. A 'read' effect implies only
    dereferencing of the resource, and not any visible mutation.
    """

    WRITE = auto()
    """
    Indicates that the operation writes to some resource. A 'write' effect implies only
    mutating a resource, and not any visible dereference or read.
    """

    ALLOC = auto()
    """
    Indicates that the operation allocates from some resource. An 'allocate' effect
    implies only allocation of the resource, and not any visible mutation or dereference.
    """

    FREE = auto()
    """
    Indicates that the operation frees some resource that has been allocated. A 'free'
    effect implies only de-allocation of the resource, and not any visible allocation,
    mutation or dereference.
    """

READ = auto() class-attribute instance-attribute

Indicates that the operation reads from some resource. A 'read' effect implies only dereferencing of the resource, and not any visible mutation.

WRITE = auto() class-attribute instance-attribute

Indicates that the operation writes to some resource. A 'write' effect implies only mutating a resource, and not any visible dereference or read.

ALLOC = auto() class-attribute instance-attribute

Indicates that the operation allocates from some resource. An 'allocate' effect implies only allocation of the resource, and not any visible mutation or dereference.

FREE = auto() class-attribute instance-attribute

Indicates that the operation frees some resource that has been allocated. A 'free' effect implies only de-allocation of the resource, and not any visible allocation, mutation or dereference.

Resource dataclass

Bases: ABC

This class represents a specific resource that an effect applies to.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class Resource(abc.ABC):
    """
    This class represents a specific resource that an effect applies to.
    """

    @abc.abstractmethod
    def name(self) -> str:
        raise NotImplementedError

__init__() -> None

name() -> str abstractmethod

Source code in xdsl/traits.py
@abc.abstractmethod
def name(self) -> str:
    raise NotImplementedError

DefaultResource dataclass

Bases: Resource

A conservative default resource kind.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class DefaultResource(Resource):
    """
    A conservative default resource kind.
    """

    def name(self) -> str:
        return "<Default>"

__init__() -> None

name() -> str

Source code in xdsl/traits.py
def name(self) -> str:
    return "<Default>"

EffectInstance dataclass

An instance of a side effect.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class EffectInstance:
    """
    An instance of a side effect.
    """

    kind: MemoryEffectKind
    """
    The kind of side effect.
    """

    value: SSAValue | SymbolRefAttr | None = field(default=None)
    """
    The value or symbol that is affected by the side effect, if known.
    """

    resource: Resource = field(default=DefaultResource())
    """
    The resource that the effect applies to.
    """

kind: MemoryEffectKind instance-attribute

The kind of side effect.

value: SSAValue | SymbolRefAttr | None = field(default=None) class-attribute instance-attribute

The value or symbol that is affected by the side effect, if known.

resource: Resource = field(default=DefaultResource()) class-attribute instance-attribute

The resource that the effect applies to.

__init__(kind: MemoryEffectKind, value: SSAValue | SymbolRefAttr | None = None, resource: Resource = DefaultResource()) -> None

MemoryEffect dataclass

Bases: OpTrait

A trait that enables operations to expose their side-effects or absence thereof.

Source code in xdsl/traits.py
class MemoryEffect(OpTrait):
    """
    A trait that enables operations to expose their side-effects or absence thereof.
    """

    @classmethod
    @abc.abstractmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance] | None:
        """
        Returns the concrete side effects of the operation.

        Return None if the operation cannot conclude - interpreted as if the operation
        had no MemoryEffect interface in the first place.
        """
        raise NotImplementedError()

get_effects(op: Operation) -> AbstractSet[EffectInstance] | None abstractmethod classmethod

Returns the concrete side effects of the operation.

Return None if the operation cannot conclude - interpreted as if the operation had no MemoryEffect interface in the first place.

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance] | None:
    """
    Returns the concrete side effects of the operation.

    Return None if the operation cannot conclude - interpreted as if the operation
    had no MemoryEffect interface in the first place.
    """
    raise NotImplementedError()

NoMemoryEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation never has side effects.

Source code in xdsl/traits.py
class NoMemoryEffect(MemoryEffect):
    """
    A trait that signals that an operation never has side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
        return frozenset()

get_effects(op: Operation) -> AbstractSet[EffectInstance] classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
    return frozenset()

MemoryReadEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation always has read side effects.

Source code in xdsl/traits.py
class MemoryReadEffect(MemoryEffect):
    """
    A trait that signals that an operation always has read side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
        return _MEMORY_READ_EFFECT_SET

get_effects(op: Operation) -> AbstractSet[EffectInstance] classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
    return _MEMORY_READ_EFFECT_SET

MemoryWriteEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation always has write side effects.

Source code in xdsl/traits.py
class MemoryWriteEffect(MemoryEffect):
    """
    A trait that signals that an operation always has write side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
        return _MEMORY_WRITE_EFFECT_SET

get_effects(op: Operation) -> AbstractSet[EffectInstance] classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
    return _MEMORY_WRITE_EFFECT_SET

MemoryAllocEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation always has alloc side effects.

Source code in xdsl/traits.py
class MemoryAllocEffect(MemoryEffect):
    """
    A trait that signals that an operation always has alloc side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
        return _MEMORY_ALLOC_EFFECT_SET

get_effects(op: Operation) -> AbstractSet[EffectInstance] classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
    return _MEMORY_ALLOC_EFFECT_SET

MemoryFreeEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation always has deallocation side effects.

Source code in xdsl/traits.py
class MemoryFreeEffect(MemoryEffect):
    """
    A trait that signals that an operation always has deallocation side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
        return _MEMORY_FREE_EFFECT_SET

get_effects(op: Operation) -> AbstractSet[EffectInstance] classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance]:
    return _MEMORY_FREE_EFFECT_SET

RecursiveMemoryEffect dataclass

Bases: MemoryEffect

A trait that signals that an operation has the side effects of its contained operations.

Source code in xdsl/traits.py
class RecursiveMemoryEffect(MemoryEffect):
    """
    A trait that signals that an operation has the side effects of its contained
    operations.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance] | None:
        effects = set[EffectInstance]()
        for r in op.regions:
            for b in r.blocks:
                for child_op in b.ops:
                    child_effects = get_effects(child_op)
                    if child_effects is None:
                        return None
                    effects.update(child_effects)
        return effects

get_effects(op: Operation) -> AbstractSet[EffectInstance] | None classmethod

Source code in xdsl/traits.py
@classmethod
def get_effects(cls, op: Operation) -> AbstractSet[EffectInstance] | None:
    effects = set[EffectInstance]()
    for r in op.regions:
        for b in r.blocks:
            for child_op in b.ops:
                child_effects = get_effects(child_op)
                if child_effects is None:
                    return None
                effects.update(child_effects)
    return effects

ConditionallySpeculatable dataclass

Bases: OpTrait

Source code in xdsl/traits.py
class ConditionallySpeculatable(OpTrait):
    @classmethod
    @abc.abstractmethod
    def is_speculatable(cls, op: Operation) -> bool:
        raise NotImplementedError()

is_speculatable(op: Operation) -> bool abstractmethod classmethod

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def is_speculatable(cls, op: Operation) -> bool:
    raise NotImplementedError()

AlwaysSpeculatable dataclass

Bases: ConditionallySpeculatable

Source code in xdsl/traits.py
class AlwaysSpeculatable(ConditionallySpeculatable):
    @classmethod
    def is_speculatable(cls, op: Operation):
        return True

is_speculatable(op: Operation) classmethod

Source code in xdsl/traits.py
@classmethod
def is_speculatable(cls, op: Operation):
    return True

RecursivelySpeculatable dataclass

Bases: ConditionallySpeculatable

Source code in xdsl/traits.py
class RecursivelySpeculatable(ConditionallySpeculatable):
    @classmethod
    def is_speculatable(cls, op: Operation):
        return all(
            is_speculatable(o) for r in op.regions for b in r.blocks for o in b.ops
        )

is_speculatable(op: Operation) classmethod

Source code in xdsl/traits.py
@classmethod
def is_speculatable(cls, op: Operation):
    return all(
        is_speculatable(o) for r in op.regions for b in r.blocks for o in b.ops
    )

Pure dataclass

Bases: NoMemoryEffect, AlwaysSpeculatable

In MLIR, Pure is NoMemoryEffect + AlwaysSpeculatable.

Source code in xdsl/traits.py
class Pure(NoMemoryEffect, AlwaysSpeculatable):
    """
    In MLIR, Pure is NoMemoryEffect + AlwaysSpeculatable.
    """

Commutative dataclass

Bases: OpTrait

A trait that signals that an operation is commutative.

Source code in xdsl/traits.py
class Commutative(OpTrait):
    """
    A trait that signals that an operation is commutative.
    """

HasInsnRepresentation dataclass

Bases: OpTrait, ABC

A trait providing information on how to encode an operation using a .insn assembler directive.

The returned string contains python string.format placeholders where formatted operands are inserted during printing.

See external documentation.

Source code in xdsl/traits.py
class HasInsnRepresentation(OpTrait, abc.ABC):
    """
    A trait providing information on how to encode an operation using a .insn assembler directive.

    The returned string contains python string.format placeholders where formatted operands are inserted during
    printing.

    See external [documentation](https://sourceware.org/binutils/docs/as/RISC_002dV_002dDirectives.html for more information.).
    """

    @abc.abstractmethod
    def get_insn(self, op: Operation) -> str:
        """
        Return the insn representation of the operation for printing.
        """
        raise NotImplementedError()

get_insn(op: Operation) -> str abstractmethod

Return the insn representation of the operation for printing.

Source code in xdsl/traits.py
@abc.abstractmethod
def get_insn(self, op: Operation) -> str:
    """
    Return the insn representation of the operation for printing.
    """
    raise NotImplementedError()

SameOperandsAndResultType dataclass

Bases: OpTrait

Constrain the operation to have the same operands and result type.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class SameOperandsAndResultType(OpTrait):
    """Constrain the operation to have the same operands and result type."""

    def verify(self, op: Operation) -> None:
        from xdsl.utils.type import (
            get_element_type_or_self,
            get_encoding,
            have_compatible_shape,
        )

        if len(op.results) < 1 or len(op.operands) < 1:
            raise VerifyException(
                f"'{op.name}' requires at least one result or operand"
            )

        result_type0 = get_element_type_or_self(op.result_types[0])

        encoding = get_encoding(op.result_types[0])

        for result_type in op.result_types[1:]:
            result_type_elem = get_element_type_or_self(result_type)
            if result_type0 != result_type_elem or not have_compatible_shape(
                op.result_types[0], result_type
            ):
                raise VerifyException(
                    f"'{op.name} requires the same type for all operands and results"
                )

            element_encoding = get_encoding(result_type)

            if encoding != element_encoding:
                raise VerifyException(
                    f"'{op.name} requires the same encoding for all operands and results"
                )

        for operand_type in op.operand_types:
            operand_type_elem = get_element_type_or_self(operand_type)
            if result_type0 != operand_type_elem or not have_compatible_shape(
                op.result_types[0], operand_type
            ):
                raise VerifyException(
                    f"'{op.name} requires the same type for all operands and results"
                )

            element_encoding = get_encoding(operand_type)

            if encoding != element_encoding:
                raise VerifyException(
                    f"'{op.name} requires the same encoding for all operands and results"
                )

__init__() -> None

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    from xdsl.utils.type import (
        get_element_type_or_self,
        get_encoding,
        have_compatible_shape,
    )

    if len(op.results) < 1 or len(op.operands) < 1:
        raise VerifyException(
            f"'{op.name}' requires at least one result or operand"
        )

    result_type0 = get_element_type_or_self(op.result_types[0])

    encoding = get_encoding(op.result_types[0])

    for result_type in op.result_types[1:]:
        result_type_elem = get_element_type_or_self(result_type)
        if result_type0 != result_type_elem or not have_compatible_shape(
            op.result_types[0], result_type
        ):
            raise VerifyException(
                f"'{op.name} requires the same type for all operands and results"
            )

        element_encoding = get_encoding(result_type)

        if encoding != element_encoding:
            raise VerifyException(
                f"'{op.name} requires the same encoding for all operands and results"
            )

    for operand_type in op.operand_types:
        operand_type_elem = get_element_type_or_self(operand_type)
        if result_type0 != operand_type_elem or not have_compatible_shape(
            op.result_types[0], operand_type
        ):
            raise VerifyException(
                f"'{op.name} requires the same type for all operands and results"
            )

        element_encoding = get_encoding(operand_type)

        if encoding != element_encoding:
            raise VerifyException(
                f"'{op.name} requires the same encoding for all operands and results"
            )

RegionSuccessor dataclass

This class represents a successor of a region. A region successor can either target another region or target an ancestor operation (at the moment, limited to the immediate parent operation). In the latter case, the control flow branches after/out of the target operation.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class RegionSuccessor:
    """
    This class represents a successor of a region. A region successor can either target another
    region or target an ancestor operation (at the moment, limited to the immediate parent
    operation). In the latter case, the control flow branches after/out of the target operation.
    """

    successor: Region | Operation

    @property
    def region(self) -> Region | None:
        """
        The successor region. None if the successor is an operation.

        Implements MLIR `RegionSuccessor::getSuccessor`.
        """
        from xdsl.ir import Region

        return self.successor if isinstance(self.successor, Region) else None

    @property
    def operation(self) -> Operation | None:
        """
        The successor operation. None if the successor is a region.

        Implements MLIR `RegionSuccessor::getSuccessorOp`.
        """
        from xdsl.ir import Operation

        return self.successor if isinstance(self.successor, Operation) else None

    def is_region(self) -> bool:
        """
        Return true if the successor is a region.

        Implements MLIR `RegionSuccessor::isRegion`.
        """
        from xdsl.ir import Region

        return isinstance(self.successor, Region)

    def is_operation(self) -> bool:
        """
        Return true if the successor is an operation.

        Implements MLIR `RegionSuccessor::isOperation`.
        """
        from xdsl.ir import Operation

        return isinstance(self.successor, Operation)

successor: Region | Operation instance-attribute

region: Region | None property

The successor region. None if the successor is an operation.

Implements MLIR RegionSuccessor::getSuccessor.

operation: Operation | None property

The successor operation. None if the successor is a region.

Implements MLIR RegionSuccessor::getSuccessorOp.

__init__(successor: Region | Operation) -> None

is_region() -> bool

Return true if the successor is a region.

Implements MLIR RegionSuccessor::isRegion.

Source code in xdsl/traits.py
def is_region(self) -> bool:
    """
    Return true if the successor is a region.

    Implements MLIR `RegionSuccessor::isRegion`.
    """
    from xdsl.ir import Region

    return isinstance(self.successor, Region)

is_operation() -> bool

Return true if the successor is an operation.

Implements MLIR RegionSuccessor::isOperation.

Source code in xdsl/traits.py
def is_operation(self) -> bool:
    """
    Return true if the successor is an operation.

    Implements MLIR `RegionSuccessor::isOperation`.
    """
    from xdsl.ir import Operation

    return isinstance(self.successor, Operation)

RegionBranchPoint dataclass

This class represents a point being branched from in the methods of the RegionBranchOpInterface.

One can branch from one of two kinds of places: * The parent operation (aka the RegionBranchOpInterface implementation) * A RegionBranchTerminatorOpInterface inside a region within the parent operation.

Construct with the terminator being branched from. Use RegionBranchPoint.PARENT to branch from the parent operation. Raises ValueError if the terminator does not implement RegionBranchTerminatorOpInterface. The constructor implements MLIR RegionBranchPoint(RegionBranchTerminatorOpInterface).

Source code in xdsl/traits.py
@dataclass(frozen=True, init=False)
class RegionBranchPoint:
    """
    This class represents a point being branched from in the methods of the
    `RegionBranchOpInterface`.

    One can branch from one of two kinds of places:
    * The parent operation (aka the `RegionBranchOpInterface` implementation)
    * A `RegionBranchTerminatorOpInterface` inside a region within the parent operation.

    Construct with the terminator being branched from. Use `RegionBranchPoint.PARENT` to branch
    from the parent operation. Raises `ValueError` if the terminator does not implement
    `RegionBranchTerminatorOpInterface`. The constructor implements MLIR
    `RegionBranchPoint(RegionBranchTerminatorOpInterface)`.
    """

    predecessor: Operation | None

    _interface: RegionBranchTerminatorOpTrait | None = field(compare=False, repr=False)
    """The `RegionBranchTerminatorOpTrait` of `predecessor`."""

    PARENT: ClassVar[RegionBranchPoint]
    """
    The `RegionBranchPoint` representing the parent operation.

    Implements MLIR `RegionBranchPoint::parent`.
    """

    def __init__(self, predecessor: Operation) -> None:
        interface = predecessor.get_trait(RegionBranchTerminatorOpTrait)
        if interface is None:
            raise ValueError(
                f"'{predecessor.name}' does not implement RegionBranchTerminatorOpInterface."
            )
        object.__setattr__(self, "predecessor", predecessor)
        object.__setattr__(self, "_interface", interface)

    def is_parent(self) -> bool:
        """
        Returns true if branching from the parent op.

        Implements MLIR `RegionBranchPoint::isParent`.
        """
        return self.predecessor is None

    @property
    def terminator(self) -> Operation | None:
        """
        The terminator if branching from a region. None otherwise.

        Implements MLIR `RegionBranchPoint::getTerminatorPredecessorOrNull`.
        """
        return self.predecessor

    @property
    def terminator_predecessor_and_trait(
        self,
    ) -> tuple[Operation, RegionBranchTerminatorOpTrait] | None:
        """
        The terminator and its `RegionBranchTerminatorOpTrait` if branching from a
        region. None otherwise.

        Implements MLIR `RegionBranchPoint::getTerminatorPredecessorOrNull`, together with
        `terminator`. MLIR returns a single interface handle that is both the op and its interface
        methods. In xDSL the op and its trait are separate objects, and the trait methods take the
        op as an argument, so this property returns both.
        """
        if self.predecessor is None or self._interface is None:
            return None
        return self.predecessor, self._interface

predecessor: Operation | None instance-attribute

PARENT: RegionBranchPoint class-attribute

The RegionBranchPoint representing the parent operation.

Implements MLIR RegionBranchPoint::parent.

terminator: Operation | None property

The terminator if branching from a region. None otherwise.

Implements MLIR RegionBranchPoint::getTerminatorPredecessorOrNull.

terminator_predecessor_and_trait: tuple[Operation, RegionBranchTerminatorOpTrait] | None property

The terminator and its RegionBranchTerminatorOpTrait if branching from a region. None otherwise.

Implements MLIR RegionBranchPoint::getTerminatorPredecessorOrNull, together with terminator. MLIR returns a single interface handle that is both the op and its interface methods. In xDSL the op and its trait are separate objects, and the trait methods take the op as an argument, so this property returns both.

__init__(predecessor: Operation) -> None

Source code in xdsl/traits.py
def __init__(self, predecessor: Operation) -> None:
    interface = predecessor.get_trait(RegionBranchTerminatorOpTrait)
    if interface is None:
        raise ValueError(
            f"'{predecessor.name}' does not implement RegionBranchTerminatorOpInterface."
        )
    object.__setattr__(self, "predecessor", predecessor)
    object.__setattr__(self, "_interface", interface)

is_parent() -> bool

Returns true if branching from the parent op.

Implements MLIR RegionBranchPoint::isParent.

Source code in xdsl/traits.py
def is_parent(self) -> bool:
    """
    Returns true if branching from the parent op.

    Implements MLIR `RegionBranchPoint::isParent`.
    """
    return self.predecessor is None

InvocationBounds dataclass

This class represents upper and lower bounds on the number of times a region of a RegionBranchOpInterface can be invoked. The lower bound is at least zero, but the upper bound may not be known.

Source code in xdsl/traits.py
@dataclass(frozen=True)
class InvocationBounds:
    """
    This class represents upper and lower bounds on the number of times a region of a
    `RegionBranchOpInterface` can be invoked. The lower bound is at least zero, but the upper bound
    may not be known.
    """

    lower: int
    """
    The minimum number of times the successor region will be invoked.

    Implements MLIR `InvocationBounds::getLowerBound`.
    """

    upper: int | None
    """
    The maximum number of times the successor region will be invoked or `None` if an upper bound
    is not known.

    Implements MLIR `InvocationBounds::getUpperBound`.
    """

    UNKNOWN: ClassVar[InvocationBounds]
    """
    The unknown invocation bounds, i.e., there is no information on how many times a region may
    be invoked.

    Implements MLIR `InvocationBounds::getUnknown`.
    """

    def __post_init__(self) -> None:
        """Implements the checks of MLIR `InvocationBounds` constructor."""
        if self.lower < 0:
            raise ValueError(
                f"Invocation lower bound must be at least 0, got {self.lower}."
            )
        if self.upper is not None and self.upper < self.lower:
            raise ValueError(
                f"Invocation upper bound ({self.upper}) cannot be less than lower bound "
                f"({self.lower})."
            )

lower: int instance-attribute

The minimum number of times the successor region will be invoked.

Implements MLIR InvocationBounds::getLowerBound.

upper: int | None instance-attribute

The maximum number of times the successor region will be invoked or None if an upper bound is not known.

Implements MLIR InvocationBounds::getUpperBound.

UNKNOWN: InvocationBounds class-attribute

The unknown invocation bounds, i.e., there is no information on how many times a region may be invoked.

Implements MLIR InvocationBounds::getUnknown.

__init__(lower: int, upper: int | None) -> None

__post_init__() -> None

Implements the checks of MLIR InvocationBounds constructor.

Source code in xdsl/traits.py
def __post_init__(self) -> None:
    """Implements the checks of MLIR `InvocationBounds` constructor."""
    if self.lower < 0:
        raise ValueError(
            f"Invocation lower bound must be at least 0, got {self.lower}."
        )
    if self.upper is not None and self.upper < self.lower:
        raise ValueError(
            f"Invocation upper bound ({self.upper}) cannot be less than lower bound "
            f"({self.lower})."
        )

RegionBranchOpTrait dataclass

Bases: OpTrait

This interface provides information for region-holding operations that exhibit branching behavior between held regions. It models the control flow edges between regions (and between the op and its regions), as well as the data flow (value propagation) that occurs along those control flow edges.

This interface is meant to model well-defined cases of control-flow and value propagation, where what occurs along control-flow edges is assumed to be side-effect free.

A "region branch point" indicates the point from which a branch (edge) originates. It can indicate: 1. A RegionBranchTerminatorOpInterface terminator in any of the immediately nested regions of this op. 2. RegionBranchPoint.PARENT: the branch originates from outside of the op, i.e., when first executing this op.

When branching from a region branch point to a region successor, the "successor operands" to be forwarded from the region branch point can be specified with get_entry_successor_operands / RegionBranchTerminatorOpInterface.get_successor_operands.

A "region successor" indicates the target of a branch. It can indicate: 1. A region of this op. 2. A parent operation, i.e., the control flow leaves/resumes after that op.

The SSA values to which successor operands are forwarded are called "successor inputs".

By default, successor operands and successor block arguments/successor results must have the same type. are_types_compatible can be implemented to allow non-equal types.

Note: This interface works in conjunction with RegionBranchTerminatorOpInterface. All immediately nested block terminators that model branching between regions must implement the RegionBranchTerminatorOpInterface. Otherwise, analyses/transformations may miss control flow edges and produce incorrect results. Not every block terminator is necessarily a region branch terminator: e.g., in the presence of unstructured control flow, a block terminator could indicate a branch to a different block within the same region.

Example:

%r = scf.for %iv = %lb to %ub step %step iter_args(%a = %b)
    -> tensor<5xf32> {
  ...
  scf.yield %c : tensor<5xf32>
}

scf.for has one region. There are two region branch points with two identical region successors: * parent => op(%r), region0(%a) * scf.yield => op(%r), region0(%a)

%a and %r are successor inputs. %b is an entry successor operand. %c is a successor operand.

Source code in xdsl/traits.py
class RegionBranchOpTrait(OpTrait):
    """
    This interface provides information for region-holding operations that exhibit branching
    behavior between held regions. It models the control flow edges between regions (and between
    the op and its regions), as well as the data flow (value propagation) that occurs along those
    control flow edges.

    This interface is meant to model well-defined cases of control-flow and value propagation,
    where what occurs along control-flow edges is assumed to be side-effect free.

    A "region branch point" indicates the point from which a branch (edge) originates. It can
    indicate:
    1. A `RegionBranchTerminatorOpInterface` terminator in any of the immediately nested regions
       of this op.
    2. `RegionBranchPoint.PARENT`: the branch originates from outside of the op, i.e., when
       first executing this op.

    When branching from a region branch point to a region successor, the "successor operands" to
    be forwarded from the region branch point can be specified with `get_entry_successor_operands`
    / `RegionBranchTerminatorOpInterface.get_successor_operands`.

    A "region successor" indicates the target of a branch. It can indicate:
    1. A region of this op.
    2. A parent operation, i.e., the control flow leaves/resumes after that op.

    The SSA values to which successor operands are forwarded are called "successor inputs".

    By default, successor operands and successor block arguments/successor results must have the
    same type. `are_types_compatible` can be implemented to allow non-equal types.

    Note: This interface works in conjunction with `RegionBranchTerminatorOpInterface`. All
    immediately nested block terminators that model branching between regions must implement the
    `RegionBranchTerminatorOpInterface`. Otherwise, analyses/transformations may miss control flow
    edges and produce incorrect results. Not every block terminator is necessarily a region branch
    terminator: e.g., in the presence of unstructured control flow, a block terminator could
    indicate a branch to a different block within the same region.

    Example:

    ```
    %r = scf.for %iv = %lb to %ub step %step iter_args(%a = %b)
        -> tensor<5xf32> {
      ...
      scf.yield %c : tensor<5xf32>
    }
    ```

    `scf.for` has one region. There are two region branch points with two identical region
    successors:
    * parent => op(%r), region0(%a)
    * `scf.yield` => op(%r), region0(%a)

    `%a` and `%r` are successor inputs. `%b` is an entry successor operand. `%c` is a successor
    operand.
    """

    @classmethod
    def entry_successor_regions(
        cls,
        op: Operation,
        operands: Sequence[Attribute | None],
    ) -> Sequence[RegionSuccessor]:
        """
        Returns all potential region successors when first executing the op.

        Unlike `successor_regions`, this method also receives the constant operands of this op
        (one entry per operand, `None` if the operand has no/unknown constant value). The
        implementation may use this information to filter out successors. By default, it simply
        dispatches to `successor_regions`.

        Note: The control flow does not necessarily have to enter any region of this op.

        Example: In the above example, this method may return two region successors: the single
        region of the `scf.for` op and the `scf.for` operation (that implements this interface). If
        `%lb`, `%ub`, `%step` are constants and it can be determined the loop does not have any
        iterations, this method may choose to return only this operation. Similarly, if it can be
        determined that the loop has at least one iteration, this method may choose to return only
        the region of the loop.
        """
        return cls.successor_regions(op, RegionBranchPoint.PARENT)

    @classmethod
    @abc.abstractmethod
    def successor_regions(
        cls, op: Operation, point: RegionBranchPoint
    ) -> Sequence[RegionSuccessor]:
        """
        Returns all potential region successors when branching from `point`. These are the
        regions that may be selected during the flow of control.

        When `point = RegionBranchPoint.PARENT`, this method returns the region successors when
        entering the operation. Otherwise, this method returns the successor regions when branching
        from the region indicated by `point`.

        Example: In the above example, this method returns the region of the `scf.for` and the
        `scf.for` op itself for either region branch point. An implementation may choose to filter
        out region successors when it is statically known (e.g., by examining the operands of this
        op) that those successors are not branched to.
        """
        raise NotImplementedError()

    @classmethod
    def successor_regions_from_region(
        cls, op: Operation, region: Region
    ) -> Iterator[RegionSuccessor]:
        """
        Returns all potential region successors when branching from any terminator in
        `region`.
        """
        for block in region.blocks:
            terminator = block.last_op
            if terminator is not None and terminator.has_trait(
                RegionBranchTerminatorOpTrait
            ):
                yield from cls.successor_regions(op, RegionBranchPoint(terminator))

    @classmethod
    def successor_inputs(
        cls,
        op: Operation,
        successor: RegionSuccessor,
    ) -> Sequence[SSAValue]:
        """
        Return all successor inputs for the given region successor. If the given region
        successor is a region, then the returned values are block arguments. Otherwise, if the
        given region successor is an operation, the returned values are op results.
        """
        return ()

    @classmethod
    def predecessors(
        cls, op: Operation, successor: RegionSuccessor
    ) -> Iterator[RegionBranchPoint]:
        """Returns all potential branching points (predecessors) for a given region successor."""
        for point in cls.all_region_branch_points(op):
            if successor in cls.successor_regions(op, point):
                yield point

    @classmethod
    def region_invocation_bounds(
        cls,
        op: Operation,
        operands: Sequence[Attribute | None],
    ) -> Sequence[InvocationBounds]:
        """
        Returns the minimum and maximum number of times this operation will invoke the attached
        regions (assuming the regions yield normally, i.e. do not abort or invoke an infinite
        loop). The minimum number of invocations is at least 0. If the maximum number of
        invocations cannot be statically determined, then it will be set to
        `InvocationBounds.UNKNOWN`.

        This method also passes along the constant operands of this op. `operands` contains an
        entry for every operand of this op, with `None` if the operand has no constant value.

        This method may be called speculatively on operations where the provided operands are not
        necessarily the same as the operation's current operands. This may occur in analyses that
        wish to determine "what would be the region invocations if these were the operands?"
        """
        return [InvocationBounds.UNKNOWN] * len(op.regions)

    @classmethod
    def are_types_compatible(
        cls, op: Operation, lhs: Attribute, rhs: Attribute
    ) -> bool:
        """
        This method is called to compare types along control-flow edges. By default, the types
        are checked as equal.
        """
        return lhs == rhs

    def verify(self, op: Operation) -> None:
        from xdsl.irdl import IRDLOperation

        # MLIR rejects ops declared with zero regions, so an op with variadic regions
        # may still have none at runtime.
        if isinstance(op, IRDLOperation) and not op.get_irdl_definition().regions:
            raise VerifyException(
                f"'{op.name}' implements RegionBranchOpInterface but defines no regions."
            )

    @classmethod
    def all_region_branch_points(cls, op: Operation) -> Iterator[RegionBranchPoint]:
        """
        Return all possible region branch points: the region branch op itself and all region
        branch terminators.
        """
        yield RegionBranchPoint.PARENT
        for region in op.regions:
            for block in region.blocks:
                terminator = block.last_op
                if terminator is not None and terminator.has_trait(
                    RegionBranchTerminatorOpTrait
                ):
                    yield RegionBranchPoint(terminator)

entry_successor_regions(op: Operation, operands: Sequence[Attribute | None]) -> Sequence[RegionSuccessor] classmethod

Returns all potential region successors when first executing the op.

Unlike successor_regions, this method also receives the constant operands of this op (one entry per operand, None if the operand has no/unknown constant value). The implementation may use this information to filter out successors. By default, it simply dispatches to successor_regions.

Note: The control flow does not necessarily have to enter any region of this op.

Example: In the above example, this method may return two region successors: the single region of the scf.for op and the scf.for operation (that implements this interface). If %lb, %ub, %step are constants and it can be determined the loop does not have any iterations, this method may choose to return only this operation. Similarly, if it can be determined that the loop has at least one iteration, this method may choose to return only the region of the loop.

Source code in xdsl/traits.py
@classmethod
def entry_successor_regions(
    cls,
    op: Operation,
    operands: Sequence[Attribute | None],
) -> Sequence[RegionSuccessor]:
    """
    Returns all potential region successors when first executing the op.

    Unlike `successor_regions`, this method also receives the constant operands of this op
    (one entry per operand, `None` if the operand has no/unknown constant value). The
    implementation may use this information to filter out successors. By default, it simply
    dispatches to `successor_regions`.

    Note: The control flow does not necessarily have to enter any region of this op.

    Example: In the above example, this method may return two region successors: the single
    region of the `scf.for` op and the `scf.for` operation (that implements this interface). If
    `%lb`, `%ub`, `%step` are constants and it can be determined the loop does not have any
    iterations, this method may choose to return only this operation. Similarly, if it can be
    determined that the loop has at least one iteration, this method may choose to return only
    the region of the loop.
    """
    return cls.successor_regions(op, RegionBranchPoint.PARENT)

successor_regions(op: Operation, point: RegionBranchPoint) -> Sequence[RegionSuccessor] abstractmethod classmethod

Returns all potential region successors when branching from point. These are the regions that may be selected during the flow of control.

When point = RegionBranchPoint.PARENT, this method returns the region successors when entering the operation. Otherwise, this method returns the successor regions when branching from the region indicated by point.

Example: In the above example, this method returns the region of the scf.for and the scf.for op itself for either region branch point. An implementation may choose to filter out region successors when it is statically known (e.g., by examining the operands of this op) that those successors are not branched to.

Source code in xdsl/traits.py
@classmethod
@abc.abstractmethod
def successor_regions(
    cls, op: Operation, point: RegionBranchPoint
) -> Sequence[RegionSuccessor]:
    """
    Returns all potential region successors when branching from `point`. These are the
    regions that may be selected during the flow of control.

    When `point = RegionBranchPoint.PARENT`, this method returns the region successors when
    entering the operation. Otherwise, this method returns the successor regions when branching
    from the region indicated by `point`.

    Example: In the above example, this method returns the region of the `scf.for` and the
    `scf.for` op itself for either region branch point. An implementation may choose to filter
    out region successors when it is statically known (e.g., by examining the operands of this
    op) that those successors are not branched to.
    """
    raise NotImplementedError()

successor_regions_from_region(op: Operation, region: Region) -> Iterator[RegionSuccessor] classmethod

Returns all potential region successors when branching from any terminator in region.

Source code in xdsl/traits.py
@classmethod
def successor_regions_from_region(
    cls, op: Operation, region: Region
) -> Iterator[RegionSuccessor]:
    """
    Returns all potential region successors when branching from any terminator in
    `region`.
    """
    for block in region.blocks:
        terminator = block.last_op
        if terminator is not None and terminator.has_trait(
            RegionBranchTerminatorOpTrait
        ):
            yield from cls.successor_regions(op, RegionBranchPoint(terminator))

successor_inputs(op: Operation, successor: RegionSuccessor) -> Sequence[SSAValue] classmethod

Return all successor inputs for the given region successor. If the given region successor is a region, then the returned values are block arguments. Otherwise, if the given region successor is an operation, the returned values are op results.

Source code in xdsl/traits.py
@classmethod
def successor_inputs(
    cls,
    op: Operation,
    successor: RegionSuccessor,
) -> Sequence[SSAValue]:
    """
    Return all successor inputs for the given region successor. If the given region
    successor is a region, then the returned values are block arguments. Otherwise, if the
    given region successor is an operation, the returned values are op results.
    """
    return ()

predecessors(op: Operation, successor: RegionSuccessor) -> Iterator[RegionBranchPoint] classmethod

Returns all potential branching points (predecessors) for a given region successor.

Source code in xdsl/traits.py
@classmethod
def predecessors(
    cls, op: Operation, successor: RegionSuccessor
) -> Iterator[RegionBranchPoint]:
    """Returns all potential branching points (predecessors) for a given region successor."""
    for point in cls.all_region_branch_points(op):
        if successor in cls.successor_regions(op, point):
            yield point

region_invocation_bounds(op: Operation, operands: Sequence[Attribute | None]) -> Sequence[InvocationBounds] classmethod

Returns the minimum and maximum number of times this operation will invoke the attached regions (assuming the regions yield normally, i.e. do not abort or invoke an infinite loop). The minimum number of invocations is at least 0. If the maximum number of invocations cannot be statically determined, then it will be set to InvocationBounds.UNKNOWN.

This method also passes along the constant operands of this op. operands contains an entry for every operand of this op, with None if the operand has no constant value.

This method may be called speculatively on operations where the provided operands are not necessarily the same as the operation's current operands. This may occur in analyses that wish to determine "what would be the region invocations if these were the operands?"

Source code in xdsl/traits.py
@classmethod
def region_invocation_bounds(
    cls,
    op: Operation,
    operands: Sequence[Attribute | None],
) -> Sequence[InvocationBounds]:
    """
    Returns the minimum and maximum number of times this operation will invoke the attached
    regions (assuming the regions yield normally, i.e. do not abort or invoke an infinite
    loop). The minimum number of invocations is at least 0. If the maximum number of
    invocations cannot be statically determined, then it will be set to
    `InvocationBounds.UNKNOWN`.

    This method also passes along the constant operands of this op. `operands` contains an
    entry for every operand of this op, with `None` if the operand has no constant value.

    This method may be called speculatively on operations where the provided operands are not
    necessarily the same as the operation's current operands. This may occur in analyses that
    wish to determine "what would be the region invocations if these were the operands?"
    """
    return [InvocationBounds.UNKNOWN] * len(op.regions)

are_types_compatible(op: Operation, lhs: Attribute, rhs: Attribute) -> bool classmethod

This method is called to compare types along control-flow edges. By default, the types are checked as equal.

Source code in xdsl/traits.py
@classmethod
def are_types_compatible(
    cls, op: Operation, lhs: Attribute, rhs: Attribute
) -> bool:
    """
    This method is called to compare types along control-flow edges. By default, the types
    are checked as equal.
    """
    return lhs == rhs

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    from xdsl.irdl import IRDLOperation

    # MLIR rejects ops declared with zero regions, so an op with variadic regions
    # may still have none at runtime.
    if isinstance(op, IRDLOperation) and not op.get_irdl_definition().regions:
        raise VerifyException(
            f"'{op.name}' implements RegionBranchOpInterface but defines no regions."
        )

all_region_branch_points(op: Operation) -> Iterator[RegionBranchPoint] classmethod

Return all possible region branch points: the region branch op itself and all region branch terminators.

Source code in xdsl/traits.py
@classmethod
def all_region_branch_points(cls, op: Operation) -> Iterator[RegionBranchPoint]:
    """
    Return all possible region branch points: the region branch op itself and all region
    branch terminators.
    """
    yield RegionBranchPoint.PARENT
    for region in op.regions:
        for block in region.blocks:
            terminator = block.last_op
            if terminator is not None and terminator.has_trait(
                RegionBranchTerminatorOpTrait
            ):
                yield RegionBranchPoint(terminator)

RegionBranchTerminatorOpTrait dataclass

Bases: OpTrait

This interface provides information for branching terminator operations in the presence of a parent RegionBranchOpInterface implementation. It acts as a marker for valid region branch points and specifies which operands are passed to which region successor.

Note: If an operation does not implement the RegionBranchTerminatorOpInterface, then that op has no region successors. (However, there may be other block terminators in the same region that implement the RegionBranchTerminatorOpInterface, so the enclosing region may have region successors.)

Source code in xdsl/traits.py
class RegionBranchTerminatorOpTrait(OpTrait):
    """
    This interface provides information for branching terminator operations in the presence of a
    parent `RegionBranchOpInterface` implementation. It acts as a marker for valid region branch
    points and specifies which operands are passed to which region successor.

    Note: If an operation does not implement the `RegionBranchTerminatorOpInterface`, then that op
    has no region successors. (However, there may be other block terminators in the same region
    that implement the `RegionBranchTerminatorOpInterface`, so the enclosing region may have region
    successors.)
    """

    def verify(self, op: Operation) -> None:
        if not op.has_trait(IsTerminator):
            raise VerifyException(f"Expected operation '{op.name}' to be a terminator.")
        if op.results:
            raise VerifyException(
                f"Expected operation '{op.name}' to have zero results."
            )
        if op.successors:
            raise VerifyException(
                f"Expected operation '{op.name}' to have zero successors."
            )

verify(op: Operation) -> None

Source code in xdsl/traits.py
def verify(self, op: Operation) -> None:
    if not op.has_trait(IsTerminator):
        raise VerifyException(f"Expected operation '{op.name}' to be a terminator.")
    if op.results:
        raise VerifyException(
            f"Expected operation '{op.name}' to have zero results."
        )
    if op.successors:
        raise VerifyException(
            f"Expected operation '{op.name}' to have zero successors."
        )

ensure_terminator(op: Operation, trait: SingleBlockImplicitTerminator) -> None

Method that helps with the creation of an implicit terminator. This should be explicitly called during the creation of an operation that has the SingleBlockImplicitTerminator trait.

Source code in xdsl/traits.py
def ensure_terminator(op: Operation, trait: SingleBlockImplicitTerminator) -> None:
    """
    Method that helps with the creation of an implicit terminator.
    This should be explicitly called during the creation of an operation that has the
    SingleBlockImplicitTerminator trait.
    """

    for region in op.regions:
        if len(region.blocks) > 1:
            raise VerifyException(f"'{op.name}' does not contain single-block regions")

        from xdsl.dialects.builtin import UnregisteredOp

        for block in region.blocks:
            if (
                (last_op := block.last_op) is not None
                and not isinstance(last_op, UnregisteredOp)
                and last_op.has_trait(IsTerminator)
                and not isinstance(last_op, trait.op_type)
            ):
                raise VerifyException(
                    f"'{op.name}' terminates with operation {last_op.name} "
                    f"instead of {trait.op_type.name}"
                )

    from xdsl.builder import ImplicitBuilder
    from xdsl.ir import Block

    for region in op.regions:
        if not region.blocks:
            region.add_block(Block())

        for block in region.blocks:
            if (last_op := block.last_op) is None or not last_op.has_trait(
                IsTerminator, value_if_unregistered=False
            ):
                with ImplicitBuilder(block):
                    trait.op_type.create()

has_effects(op: Operation, effect: MemoryEffectKind) -> bool

Returns if the operation has side effects of this kind.

Source code in xdsl/traits.py
def has_effects(op: Operation, effect: MemoryEffectKind) -> bool:
    """
    Returns if the operation has side effects of this kind.
    """
    effects = get_effects(op)
    return effects is not None and any(e.kind == effect for e in effects)

has_exact_effect(op: Operation, effect: MemoryEffectKind) -> bool

Returns if the operation has the given side effects and no others.

proxy for only_has_effect

Source code in xdsl/traits.py
def has_exact_effect(op: Operation, effect: MemoryEffectKind) -> bool:
    """
    Returns if the operation has the given side effects and no others.

    proxy for only_has_effect
    """
    return only_has_effect(op, effect)

only_has_effect(op: Operation, effect: MemoryEffectKind) -> bool

Returns if the operation has the given side effects and no others.

Source code in xdsl/traits.py
def only_has_effect(op: Operation, effect: MemoryEffectKind) -> bool:
    """
    Returns if the operation has the given side effects and no others.
    """
    effects = get_effects(op)
    return effects is not None and all(e.kind == effect for e in effects)

is_side_effect_free(op: Operation) -> bool

Boilerplate helper to check if a generic operation is side effect free for sure.

Source code in xdsl/traits.py
def is_side_effect_free(op: Operation) -> bool:
    """
    Boilerplate helper to check if a generic operation is side effect free for sure.
    """
    effects = get_effects(op)
    return effects is not None and len(effects) == 0

get_effects(op: Operation) -> AbstractSet[EffectInstance] | None

Helper to get known side effects of an operation. None means that the operation has unknown effects, for safety.

Source code in xdsl/traits.py
def get_effects(op: Operation) -> AbstractSet[EffectInstance] | None:
    """
    Helper to get known side effects of an operation.
    None means that the operation has unknown effects, for safety.
    """

    effect_interfaces = op.get_traits_of_type(MemoryEffect)
    if not effect_interfaces:
        return None

    effects = set[EffectInstance]()
    for it in op.get_traits_of_type(MemoryEffect):
        it_effects = it.get_effects(op)
        if it_effects is None:
            return None
        effects.update(it_effects)

    return effects

is_speculatable(op: Operation)

Source code in xdsl/traits.py
def is_speculatable(op: Operation):
    trait = op.get_trait(ConditionallySpeculatable)
    return (trait is not None) and trait.is_speculatable(op)