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
19
20
21
22
23
24
25
26
27
28
29
30
@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
28
29
30
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
71
72
73
74
75
76
77
78
79
@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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
@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
88
89
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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
@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
115
116
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
118
119
120
121
122
123
124
125
126
127
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
129
130
131
132
133
134
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
136
137
138
139
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
150
151
152
153
154
155
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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
166
167
168
169
170
171
172
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
175
176
177
178
179
180
181
182
183
184
185
186
187
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
182
183
184
185
186
187
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
@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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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.
                        if not op.is_ancestor(operand.owner):
                            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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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.
                    if not op.is_ancestor(operand.owner):
                        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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
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
310
311
312
313
314
315
316
317
318
@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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
@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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
@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
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
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
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
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
459
460
461
462
463
464
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
466
467
468
469
470
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
473
474
475
476
477
478
479
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
478
479
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
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
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
492
493
494
495
496
497
498
@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
500
501
502
503
@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
505
506
507
508
@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
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
@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
519
520
521
522
523
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
525
526
527
528
@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
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
@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
539
540
def verify(self, op: Operation) -> None:
    return

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

Source code in xdsl/traits.py
542
543
544
545
@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 specfic resources or values. Here, everything has its effect on the universe.

Source code in xdsl/traits.py
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
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 specfic 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
582
583
584
585
586
587
588
589
590
@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
588
589
590
@abc.abstractmethod
def name(self) -> str:
    raise NotImplementedError

DefaultResource dataclass

Bases: Resource

A conservative default resource kind.

Source code in xdsl/traits.py
593
594
595
596
597
598
599
600
@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
599
600
def name(self) -> str:
    return "<Default>"

EffectInstance dataclass

An instance of a side effect.

Source code in xdsl/traits.py
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
@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
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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) -> set[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) -> set[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
630
631
632
633
634
635
636
637
638
639
@classmethod
@abc.abstractmethod
def get_effects(cls, op: Operation) -> set[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
695
696
697
698
699
700
701
702
class NoMemoryEffect(MemoryEffect):
    """
    A trait that signals that an operation never has side effects.
    """

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

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

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

MemoryReadEffect dataclass

Bases: MemoryEffect

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

Source code in xdsl/traits.py
705
706
707
708
709
710
711
712
class MemoryReadEffect(MemoryEffect):
    """
    A trait that signals that an operation always has read side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> set[EffectInstance]:
        return {EffectInstance(MemoryEffectKind.READ)}

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

Source code in xdsl/traits.py
710
711
712
@classmethod
def get_effects(cls, op: Operation) -> set[EffectInstance]:
    return {EffectInstance(MemoryEffectKind.READ)}

MemoryWriteEffect dataclass

Bases: MemoryEffect

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

Source code in xdsl/traits.py
715
716
717
718
719
720
721
722
class MemoryWriteEffect(MemoryEffect):
    """
    A trait that signals that an operation always has write side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> set[EffectInstance]:
        return {EffectInstance(MemoryEffectKind.WRITE)}

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

Source code in xdsl/traits.py
720
721
722
@classmethod
def get_effects(cls, op: Operation) -> set[EffectInstance]:
    return {EffectInstance(MemoryEffectKind.WRITE)}

MemoryAllocEffect dataclass

Bases: MemoryEffect

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

Source code in xdsl/traits.py
725
726
727
728
729
730
731
732
class MemoryAllocEffect(MemoryEffect):
    """
    A trait that signals that an operation always has alloc side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> set[EffectInstance]:
        return {EffectInstance(MemoryEffectKind.ALLOC)}

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

Source code in xdsl/traits.py
730
731
732
@classmethod
def get_effects(cls, op: Operation) -> set[EffectInstance]:
    return {EffectInstance(MemoryEffectKind.ALLOC)}

MemoryFreeEffect dataclass

Bases: MemoryEffect

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

Source code in xdsl/traits.py
735
736
737
738
739
740
741
742
class MemoryFreeEffect(MemoryEffect):
    """
    A trait that signals that an operation always has deallocation side effects.
    """

    @classmethod
    def get_effects(cls, op: Operation) -> set[EffectInstance]:
        return {EffectInstance(MemoryEffectKind.FREE)}

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

Source code in xdsl/traits.py
740
741
742
@classmethod
def get_effects(cls, op: Operation) -> set[EffectInstance]:
    return {EffectInstance(MemoryEffectKind.FREE)}

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
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
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):
        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) classmethod

Source code in xdsl/traits.py
751
752
753
754
755
756
757
758
759
760
761
@classmethod
def get_effects(cls, op: Operation):
    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
764
765
766
767
768
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
765
766
767
768
@classmethod
@abc.abstractmethod
def is_speculatable(cls, op: Operation) -> bool:
    raise NotImplementedError()

AlwaysSpeculatable dataclass

Bases: ConditionallySpeculatable

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

is_speculatable(op: Operation) classmethod

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

RecursivelySpeculatable dataclass

Bases: ConditionallySpeculatable

Source code in xdsl/traits.py
777
778
779
780
781
782
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
778
779
780
781
782
@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
790
791
792
793
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
796
797
798
799
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 assember 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
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
class HasInsnRepresentation(OpTrait, abc.ABC):
    """
    A trait providing information on how to encode an operation using a .insn assember 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
812
813
814
815
816
817
@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
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
@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
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
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"
            )

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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
642
643
644
645
646
647
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
650
651
652
653
654
655
656
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
659
660
661
662
663
664
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
667
668
669
670
671
672
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) -> set[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
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
def get_effects(op: Operation) -> set[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
785
786
787
def is_speculatable(op: Operation):
    trait = op.get_trait(ConditionallySpeculatable)
    return (trait is not None) and trait.is_speculatable(op)