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.

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
class ConstantLike(OpTrait, abc.ABC):
    """
    Operation known to be constant-like.

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

    @classmethod
    @abc.abstractmethod
    def get_constant_value(cls, op: Operation) -> Attribute:
        """
        Get the constant value from this constant-like operation.

        Returns:
            The constant value as an Attribute, or None if the value cannot be determined.
        """
        raise NotImplementedError()

get_constant_value(op: Operation) -> Attribute abstractmethod classmethod

Get the constant value from this constant-like operation.

Returns:

Type Description
Attribute

The constant value as an Attribute, or None if the value cannot be determined.

Source code in xdsl/traits.py
43
44
45
46
47
48
49
50
51
52
@classmethod
@abc.abstractmethod
def get_constant_value(cls, op: Operation) -> Attribute:
    """
    Get the constant value from this constant-like operation.

    Returns:
        The constant value as an Attribute, or None if the value cannot be determined.
    """
    raise NotImplementedError()

HasFolder dataclass

Bases: OpTrait

Operation known to support folding.

Source code in xdsl/traits.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
60
61
62
63
64
65
66
67
68
@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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
@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
77
78
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
80
81
82
83
84
85
86
87
88
89
90
91
92
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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@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
104
105
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
107
108
109
110
111
112
113
114
115
116
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
118
119
120
121
122
123
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
125
126
127
128
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
139
140
141
142
143
144
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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
155
156
157
158
159
160
161
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
164
165
166
167
168
169
170
171
172
173
174
175
176
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
171
172
173
174
175
176
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
@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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
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
299
300
301
302
303
304
305
306
307
@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
310
311
312
313
314
315
316
317
318
319
320
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
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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
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
@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
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
@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
413
414
415
416
417
418
419
420
421
422
423
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
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
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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
448
449
450
451
452
453
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
455
456
457
458
459
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
462
463
464
465
466
467
468
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
467
468
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
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
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
481
482
483
484
485
486
487
@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
489
490
491
492
@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
494
495
496
497
@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
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
@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
508
509
510
511
512
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
514
515
516
517
@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
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
@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
528
529
def verify(self, op: Operation) -> None:
    return

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

Source code in xdsl/traits.py
531
532
533
534
@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
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
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
571
572
573
574
575
576
577
578
579
@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
577
578
579
@abc.abstractmethod
def name(self) -> str:
    raise NotImplementedError

DefaultResource dataclass

Bases: Resource

A conservative default resource kind.

Source code in xdsl/traits.py
582
583
584
585
586
587
588
589
@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
588
589
def name(self) -> str:
    return "<Default>"

EffectInstance dataclass

An instance of a side effect.

Source code in xdsl/traits.py
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
@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
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
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
619
620
621
622
623
624
625
626
627
628
@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
684
685
686
687
688
689
690
691
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
689
690
691
@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
694
695
696
697
698
699
700
701
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
699
700
701
@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
704
705
706
707
708
709
710
711
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
709
710
711
@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
714
715
716
717
718
719
720
721
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
719
720
721
@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
724
725
726
727
728
729
730
731
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
729
730
731
@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
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
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
740
741
742
743
744
745
746
747
748
749
750
@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
753
754
755
756
757
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
754
755
756
757
@classmethod
@abc.abstractmethod
def is_speculatable(cls, op: Operation) -> bool:
    raise NotImplementedError()

AlwaysSpeculatable dataclass

Bases: ConditionallySpeculatable

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

is_speculatable(op: Operation) classmethod

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

RecursivelySpeculatable dataclass

Bases: ConditionallySpeculatable

Source code in xdsl/traits.py
766
767
768
769
770
771
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
767
768
769
770
771
@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, but the latter is nowhere to be found here.

Source code in xdsl/traits.py
779
780
781
782
783
class Pure(NoMemoryEffect, AlwaysSpeculatable):
    """
    In MLIR, Pure is NoMemoryEffect + AlwaysSpeculatable, but the latter is nowhere to be
    found here.
    """

Commutative dataclass

Bases: OpTrait

A trait that signals that an operation is commutative.

Source code in xdsl/traits.py
786
787
788
789
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
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
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
802
803
804
805
806
807
@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
810
811
812
813
814
815
816
817
818
819
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
@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
814
815
816
817
818
819
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
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
214
215
216
217
218
219
220
221
222
223
224
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
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
631
632
633
634
635
636
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
639
640
641
642
643
644
645
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
648
649
650
651
652
653
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
656
657
658
659
660
661
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
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
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
774
775
776
def is_speculatable(op: Operation):
    trait = op.get_trait(ConditionallySpeculatable)
    return (trait is not None) and trait.is_speculatable(op)