Skip to content

Builder

builder

InsertOpInvT = TypeVar('InsertOpInvT', bound=(Operation | Sequence[Operation])) module-attribute

BuilderListener dataclass

A listener for builder events.

Source code in xdsl/builder.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@dataclass(eq=False)
class BuilderListener:
    """A listener for builder events."""

    operation_insertion_handler: list[Callable[[Operation], None]] = field(
        default_factory=list[Callable[[Operation], None]], kw_only=True
    )
    """Callbacks that are called when an operation is inserted by the builder."""

    block_creation_handler: list[Callable[[Block], None]] = field(
        default_factory=list[Callable[[Block], None]], kw_only=True
    )
    """Callback that are called when a block is created by the builder."""

    def handle_operation_insertion(self, op: Operation) -> None:
        """Pass the operation that was just inserted to callbacks."""
        for callback in self.operation_insertion_handler:
            callback(op)

    def handle_block_creation(self, block: Block) -> None:
        """Pass the block that was just created to callbacks."""
        for callback in self.block_creation_handler:
            callback(block)

    def extend_from_listener(self, listener: BuilderListener) -> None:
        """Forward all callbacks from `listener` to this listener."""
        self.operation_insertion_handler.extend(listener.operation_insertion_handler)
        self.block_creation_handler.extend(listener.block_creation_handler)

operation_insertion_handler: list[Callable[[Operation], None]] = field(default_factory=(list[Callable[[Operation], None]]), kw_only=True) class-attribute instance-attribute

Callbacks that are called when an operation is inserted by the builder.

block_creation_handler: list[Callable[[Block], None]] = field(default_factory=(list[Callable[[Block], None]]), kw_only=True) class-attribute instance-attribute

Callback that are called when a block is created by the builder.

__init__(*, operation_insertion_handler: list[Callable[[Operation], None]] = list[Callable[[Operation], None]](), block_creation_handler: list[Callable[[Block], None]] = list[Callable[[Block], None]]()) -> None

handle_operation_insertion(op: Operation) -> None

Pass the operation that was just inserted to callbacks.

Source code in xdsl/builder.py
38
39
40
41
def handle_operation_insertion(self, op: Operation) -> None:
    """Pass the operation that was just inserted to callbacks."""
    for callback in self.operation_insertion_handler:
        callback(op)

handle_block_creation(block: Block) -> None

Pass the block that was just created to callbacks.

Source code in xdsl/builder.py
43
44
45
46
def handle_block_creation(self, block: Block) -> None:
    """Pass the block that was just created to callbacks."""
    for callback in self.block_creation_handler:
        callback(block)

extend_from_listener(listener: BuilderListener) -> None

Forward all callbacks from listener to this listener.

Source code in xdsl/builder.py
48
49
50
51
def extend_from_listener(self, listener: BuilderListener) -> None:
    """Forward all callbacks from `listener` to this listener."""
    self.operation_insertion_handler.extend(listener.operation_insertion_handler)
    self.block_creation_handler.extend(listener.block_creation_handler)

Builder dataclass

Bases: BuilderListener

A helper class to construct IRs, by keeping track of where to insert an operation. It mimics the OpBuilder class from MLIR.

See external documentation.

Source code in xdsl/builder.py
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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
212
213
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
252
253
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
@dataclass
class Builder(BuilderListener):
    """
    A helper class to construct IRs, by keeping track of where to insert an
    operation. It mimics the OpBuilder class from MLIR.

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

    insertion_point: InsertPoint
    """Operations will be inserted at this location."""

    _name_hint: str | None = field(default=None)
    """
    If this is not None, results of inserted operations will be assigned this name hint.
    This is always valid.
    """

    @property
    def name_hint(self) -> str | None:
        return self._name_hint

    @name_hint.setter
    def name_hint(self, name: str | None):
        self._name_hint = SSAValue.extract_valid_name(name)

    def insert(self, op: OperationInvT) -> OperationInvT:
        """
        Inserts op at the current location and returns it.
        """
        return self.insert_op(op)

    def insert_op(
        self,
        op: InsertOpInvT,
        insertion_point: InsertPoint | None = None,
    ) -> InsertOpInvT:
        """Inserts op(s) at the current insertion point."""
        ops = (op,) if isinstance(op, Operation) else op
        if not ops:
            return ops

        implicit_builder = _current_builder.builder
        if implicit_builder is not None and implicit_builder is not self:
            raise ValueError(
                "Cannot insert operation explicitly when an implicit builder exists."
            )

        Rewriter.insert_op(
            op, self.insertion_point if insertion_point is None else insertion_point
        )

        for op_ in ops:
            if self._name_hint is not None:
                for result in op_.results:
                    if result.name_hint is None:
                        result._name = self._name_hint  # pyright: ignore[reportPrivateUsage]
            self.handle_operation_insertion(op_)

        return op

    def create_block(
        self, insert_point: BlockInsertPoint, arg_types: Iterable[Attribute] = ()
    ) -> Block:
        """
        Create a block at the given location, and set the operation insertion point
        at the end of the inserted block.
        """
        block = Block(arg_types=arg_types)
        Rewriter.insert_block(block, insert_point)

        self.insertion_point = InsertPoint.at_end(block)

        self.handle_block_creation(block)
        return block

    @staticmethod
    def _region_no_args(func: Callable[[Builder], None]) -> Region:
        """
        Generates a single-block region.
        """
        block = Block()
        builder = Builder(InsertPoint.at_end(block))
        func(builder)
        return Region(block)

    @staticmethod
    def _region_args(
        input_types: Sequence[Attribute] | ArrayAttr[Attribute],
    ) -> Callable[[_CallableRegionFuncType], Region]:
        """
        Decorator for constructing a single-block region, containing the implementation of a
        region with some input arguments.
        """

        if isinstance(input_types, ArrayAttr):
            input_types = input_types.data

        def wrapper(func: _CallableRegionFuncType) -> Region:
            block = Block(arg_types=input_types)
            builder = Builder(InsertPoint.at_start(block))

            func(builder, block.args)

            region = Region(block)
            return region

        return wrapper

    @overload
    @staticmethod
    def region(
        input: Sequence[Attribute] | ArrayAttr[Attribute],
    ) -> Callable[[_CallableRegionFuncType], Region]:
        """
        Annotation used to construct a Region tuple from a function.
        The annotation can be used in two ways:

        For regions that have inputs or outputs:
        ```
        @Builder.region(input_types)
        def func(builder: Builder, args: tuple[BlockArgument, ...]) -> None:
            ...
        ```

        For regions that don't have inputs or outputs:
        ``` python
        @Builder.region
        def func(builder: Builder) -> None:
            ...
        ```
        """
        ...

    @overload
    @staticmethod
    def region(input: Callable[[Builder], None]) -> Region: ...

    @staticmethod
    def region(
        input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[Builder], None],
    ) -> Callable[[_CallableRegionFuncType], Region] | Region:
        if isinstance(input, Callable):
            return Builder._region_no_args(input)
        else:
            return Builder._region_args(input)

    @staticmethod
    def _implicit_region_no_args(func: Callable[[], None]) -> Region:
        """
        Generates a single-block region.
        """
        block = Block()
        builder = Builder(InsertPoint.at_end(block))

        with ImplicitBuilder(builder):
            func()

        return Region(block)

    @staticmethod
    def _implicit_region_args(
        input_types: Sequence[Attribute] | ArrayAttr[Attribute],
    ) -> Callable[[_CallableImplicitRegionFuncType], Region]:
        """
        Decorator for constructing a single-block region, containing the implementation of a
        region with some input arguments.
        """

        if isinstance(input_types, ArrayAttr):
            input_types = input_types.data

        def wrapper(func: _CallableImplicitRegionFuncType) -> Region:
            block = Block(arg_types=input_types)
            builder = Builder(InsertPoint.at_end(block))

            with ImplicitBuilder(builder):
                func(block.args)

            region = Region(block)
            return region

        return wrapper

    @overload
    @staticmethod
    def implicit_region(
        input: Sequence[Attribute] | ArrayAttr[Attribute],
    ) -> Callable[[_CallableImplicitRegionFuncType], Region]:
        """
        Annotation used to construct a Region tuple from a function.
        The annotation can be used in two ways:

        For regions that have inputs or outputs:
        ```
        @Builder.implicit_region(input_types)
        def func(args: tuple[BlockArgument, ...]) -> None:
            ...
        ```

        For regions that don't have inputs or outputs:
        ``` python
        @Builder.implicit_region
        def func() -> None:
            ...
        ```
        """
        ...

    @overload
    @staticmethod
    def implicit_region(input: Callable[[], None]) -> Region: ...

    @staticmethod
    def implicit_region(
        input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[], None],
    ) -> Callable[[_CallableImplicitRegionFuncType], Region] | Region:
        if isinstance(input, Callable):
            return Builder._implicit_region_no_args(input)
        else:
            return Builder._implicit_region_args(input)

    @staticmethod
    def assert_implicit():
        if _current_builder.builder is None:
            raise ValueError(
                "op_builder must be called within an implicit builder block"
            )

insertion_point: InsertPoint instance-attribute

Operations will be inserted at this location.

name_hint: str | None property writable

__init__(insertion_point: InsertPoint, _name_hint: str | None = None, *, operation_insertion_handler: list[Callable[[Operation], None]] = list[Callable[[Operation], None]](), block_creation_handler: list[Callable[[Block], None]] = list[Callable[[Block], None]]()) -> None

insert(op: OperationInvT) -> OperationInvT

Inserts op at the current location and returns it.

Source code in xdsl/builder.py
83
84
85
86
87
def insert(self, op: OperationInvT) -> OperationInvT:
    """
    Inserts op at the current location and returns it.
    """
    return self.insert_op(op)

insert_op(op: InsertOpInvT, insertion_point: InsertPoint | None = None) -> InsertOpInvT

Inserts op(s) at the current insertion point.

Source code in xdsl/builder.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def insert_op(
    self,
    op: InsertOpInvT,
    insertion_point: InsertPoint | None = None,
) -> InsertOpInvT:
    """Inserts op(s) at the current insertion point."""
    ops = (op,) if isinstance(op, Operation) else op
    if not ops:
        return ops

    implicit_builder = _current_builder.builder
    if implicit_builder is not None and implicit_builder is not self:
        raise ValueError(
            "Cannot insert operation explicitly when an implicit builder exists."
        )

    Rewriter.insert_op(
        op, self.insertion_point if insertion_point is None else insertion_point
    )

    for op_ in ops:
        if self._name_hint is not None:
            for result in op_.results:
                if result.name_hint is None:
                    result._name = self._name_hint  # pyright: ignore[reportPrivateUsage]
        self.handle_operation_insertion(op_)

    return op

create_block(insert_point: BlockInsertPoint, arg_types: Iterable[Attribute] = ()) -> Block

Create a block at the given location, and set the operation insertion point at the end of the inserted block.

Source code in xdsl/builder.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def create_block(
    self, insert_point: BlockInsertPoint, arg_types: Iterable[Attribute] = ()
) -> Block:
    """
    Create a block at the given location, and set the operation insertion point
    at the end of the inserted block.
    """
    block = Block(arg_types=arg_types)
    Rewriter.insert_block(block, insert_point)

    self.insertion_point = InsertPoint.at_end(block)

    self.handle_block_creation(block)
    return block

region(input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[Builder], None]) -> Callable[[_CallableRegionFuncType], Region] | Region staticmethod

region(
    input: Sequence[Attribute] | ArrayAttr[Attribute],
) -> Callable[[_CallableRegionFuncType], Region]
region(input: Callable[[Builder], None]) -> Region
Source code in xdsl/builder.py
195
196
197
198
199
200
201
202
@staticmethod
def region(
    input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[Builder], None],
) -> Callable[[_CallableRegionFuncType], Region] | Region:
    if isinstance(input, Callable):
        return Builder._region_no_args(input)
    else:
        return Builder._region_args(input)

implicit_region(input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[], None]) -> Callable[[_CallableImplicitRegionFuncType], Region] | Region staticmethod

implicit_region(
    input: Sequence[Attribute] | ArrayAttr[Attribute],
) -> Callable[[_CallableImplicitRegionFuncType], Region]
implicit_region(input: Callable[[], None]) -> Region
Source code in xdsl/builder.py
270
271
272
273
274
275
276
277
@staticmethod
def implicit_region(
    input: Sequence[Attribute] | ArrayAttr[Attribute] | Callable[[], None],
) -> Callable[[_CallableImplicitRegionFuncType], Region] | Region:
    if isinstance(input, Callable):
        return Builder._implicit_region_no_args(input)
    else:
        return Builder._implicit_region_args(input)

assert_implicit() staticmethod

Source code in xdsl/builder.py
279
280
281
282
283
284
@staticmethod
def assert_implicit():
    if _current_builder.builder is None:
        raise ValueError(
            "op_builder must be called within an implicit builder block"
        )

ImplicitBuilder(arg: Builder | Block | Region | None)

Context manager for managing the current implicit builder context, consisting of the stack of builders in the current thread, and the current builder.

Operations created within a with block of an implicit builder will be added to it. If there are nested implicit builder blocks, the operation will be added to the innermost one. Operations cannot be added to multiple blocks, and any attempt to do so will result in an exception.

Example:

from xdsl.dialects import arith

with ImplicitBuilder(block):
    arith.Constant(IntegerAttr(5, 32))

assert len(block.ops) == 1
assert isinstance(block.ops.first, arith.Constant)
Source code in xdsl/builder.py
303
304
305
306
307
308
309
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
@contextlib.contextmanager
def ImplicitBuilder(
    arg: Builder | Block | Region | None,
):
    """
    Context manager for managing the current implicit builder context, consisting of the stack of builders in
    the current thread, and the current builder.

    Operations created within a `with` block of an implicit builder will be added to it.
    If there are nested implicit builder blocks, the operation will be added to the
    innermost one. Operations cannot be added to multiple blocks, and any attempt to do so
    will result in an exception.

    Example:

    ``` python
    from xdsl.dialects import arith

    with ImplicitBuilder(block):
        arith.Constant(IntegerAttr(5, 32))

    assert len(block.ops) == 1
    assert isinstance(block.ops.first, arith.Constant)
    ```
    """
    match arg:
        case None:
            # None option added as convenience to allow for extending optional regions
            # in ops easily.
            raise ValueError("Cannot pass None to implicit_builder")
        case Region():
            builder = Builder(InsertPoint.at_end(arg.block))
        case Block():
            builder = Builder(InsertPoint.at_end(arg))
        case Builder():
            builder = arg

    old_builder = _current_builder.builder
    old_post_init = None
    if old_builder is None:
        old_post_init = _override_operation_post_init()

    _current_builder.builder = builder
    try:
        yield builder.insertion_point.block.args
    finally:
        _current_builder.builder = old_builder
        if old_builder is None:
            Operation.__post_init__ = old_post_init  # pyright: ignore[reportAttributeAccessIssue]