Skip to content

Func

func

Func = Dialect('func', [FuncOp, CallOp, ReturnOp], []) module-attribute

FuncOpCallableInterface dataclass

Bases: CallableOpInterface

Source code in xdsl/dialects/func.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class FuncOpCallableInterface(CallableOpInterface):
    @classmethod
    def get_callable_region(cls, op: Operation) -> Region:
        assert isinstance(op, FuncOp)
        return op.body

    @classmethod
    def get_argument_types(cls, op: Operation) -> tuple[Attribute, ...]:
        assert isinstance(op, FuncOp)
        return op.function_type.inputs.data

    @classmethod
    def get_result_types(cls, op: Operation) -> tuple[Attribute, ...]:
        assert isinstance(op, FuncOp)
        return op.function_type.outputs.data

get_callable_region(op: Operation) -> Region classmethod

Source code in xdsl/dialects/func.py
55
56
57
58
@classmethod
def get_callable_region(cls, op: Operation) -> Region:
    assert isinstance(op, FuncOp)
    return op.body

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

Source code in xdsl/dialects/func.py
60
61
62
63
@classmethod
def get_argument_types(cls, op: Operation) -> tuple[Attribute, ...]:
    assert isinstance(op, FuncOp)
    return op.function_type.inputs.data

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

Source code in xdsl/dialects/func.py
65
66
67
68
@classmethod
def get_result_types(cls, op: Operation) -> tuple[Attribute, ...]:
    assert isinstance(op, FuncOp)
    return op.function_type.outputs.data

CallOpSymbolUserOpInterface dataclass

Bases: SymbolUserOpInterface

Source code in xdsl/dialects/func.py
 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
class CallOpSymbolUserOpInterface(SymbolUserOpInterface):
    def verify(self, op: Operation) -> None:
        assert isinstance(op, CallOp)

        found_callee = SymbolTable.lookup_symbol(op, op.callee)
        if not found_callee:
            raise VerifyException(f"'{op.callee}' could not be found in symbol table")

        if not isinstance(found_callee, FuncOp):
            raise VerifyException(f"'{op.callee}' does not reference a valid function")

        if len(found_callee.function_type.inputs) != len(op.arguments):
            raise VerifyException("incorrect number of operands for callee")

        if len(found_callee.function_type.outputs) != len(op.result_types):
            raise VerifyException("incorrect number of results for callee")

        for idx, (found_operand, operand) in enumerate(
            zip(found_callee.function_type.inputs, (arg.type for arg in op.arguments))
        ):
            if found_operand != operand:
                raise VerifyException(
                    f"operand type mismatch: expected operand type {found_operand}, "
                    f"but provided {operand} for operand number {idx}"
                )

        for idx, (found_res, res) in enumerate(
            zip(found_callee.function_type.outputs, op.result_types)
        ):
            if found_res != res:
                raise VerifyException(
                    f"result type mismatch: expected result type {found_res}, but "
                    f"provided {res} for result number {idx}"
                )

        return

verify(op: Operation) -> None

Source code in xdsl/dialects/func.py
 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
def verify(self, op: Operation) -> None:
    assert isinstance(op, CallOp)

    found_callee = SymbolTable.lookup_symbol(op, op.callee)
    if not found_callee:
        raise VerifyException(f"'{op.callee}' could not be found in symbol table")

    if not isinstance(found_callee, FuncOp):
        raise VerifyException(f"'{op.callee}' does not reference a valid function")

    if len(found_callee.function_type.inputs) != len(op.arguments):
        raise VerifyException("incorrect number of operands for callee")

    if len(found_callee.function_type.outputs) != len(op.result_types):
        raise VerifyException("incorrect number of results for callee")

    for idx, (found_operand, operand) in enumerate(
        zip(found_callee.function_type.inputs, (arg.type for arg in op.arguments))
    ):
        if found_operand != operand:
            raise VerifyException(
                f"operand type mismatch: expected operand type {found_operand}, "
                f"but provided {operand} for operand number {idx}"
            )

    for idx, (found_res, res) in enumerate(
        zip(found_callee.function_type.outputs, op.result_types)
    ):
        if found_res != res:
            raise VerifyException(
                f"result type mismatch: expected result type {found_res}, but "
                f"provided {res} for result number {idx}"
            )

    return

FuncOp

Bases: IRDLOperation

Source code in xdsl/dialects/func.py
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@irdl_op_definition
class FuncOp(IRDLOperation):
    name = "func.func"

    body = region_def()
    sym_name = prop_def(SymbolNameConstraint())
    function_type = prop_def(FunctionType)
    sym_visibility = opt_prop_def(StringAttr)
    arg_attrs = opt_prop_def(ArrayAttr[DictionaryAttr])
    res_attrs = opt_prop_def(ArrayAttr[DictionaryAttr])

    traits = traits_def(
        IsolatedFromAbove(), SymbolOpInterface(), FuncOpCallableInterface()
    )

    def __init__(
        self,
        name: str,
        function_type: FunctionType | tuple[Sequence[Attribute], Sequence[Attribute]],
        region: Region | type[Region.DEFAULT] = Region.DEFAULT,
        visibility: StringAttr | str | None = None,
        *,
        arg_attrs: ArrayAttr[DictionaryAttr] | None = None,
        res_attrs: ArrayAttr[DictionaryAttr] | None = None,
    ):
        if isinstance(visibility, str):
            visibility = StringAttr(visibility)
        if isinstance(function_type, tuple):
            inputs, outputs = function_type
            function_type = FunctionType.from_lists(inputs, outputs)
        if not isinstance(region, Region):
            region = Region(Block(arg_types=function_type.inputs))
        properties: dict[str, Attribute | None] = {
            "sym_name": StringAttr(name),
            "function_type": function_type,
            "sym_visibility": visibility,
            "arg_attrs": arg_attrs,
            "res_attrs": res_attrs,
        }
        super().__init__(properties=properties, regions=[region])

    def verify_(self) -> None:
        # If this is an empty region (external function), then return
        if len(self.body.blocks) == 0:
            return

        entry_block = self.body.blocks.first
        assert entry_block is not None
        block_arg_types = entry_block.arg_types
        if self.function_type.inputs.data != tuple(block_arg_types):
            raise VerifyException(
                "Expected entry block arguments to have the same types as the function "
                "input types"
            )

    @classmethod
    def parse(cls, parser: Parser) -> FuncOp:
        visibility = parser.parse_optional_visibility_keyword()

        (
            name,
            input_types,
            return_types,
            region,
            extra_attrs,
            arg_attrs,
            res_attrs,
        ) = parse_func_op_like(
            parser, reserved_attr_names=("sym_name", "function_type", "sym_visibility")
        )
        func = FuncOp(
            name=name,
            function_type=(input_types, return_types),
            region=region,
            visibility=visibility,
            arg_attrs=arg_attrs,
            res_attrs=res_attrs,
        )
        if extra_attrs is not None:
            func.attributes |= extra_attrs.data
        return func

    def print(self, printer: Printer):
        if self.sym_visibility:
            visibility = self.sym_visibility.data
            printer.print_string(" ")
            printer.print_string(visibility)

        print_func_op_like(
            printer,
            self.sym_name,
            self.function_type,
            self.body,
            self.attributes,
            arg_attrs=self.arg_attrs,
            res_attrs=self.res_attrs,
            reserved_attr_names=(
                "sym_name",
                "function_type",
                "sym_visibility",
                "arg_attrs",
            ),
        )

    @staticmethod
    def external(
        name: str, input_types: Sequence[Attribute], return_types: Sequence[Attribute]
    ) -> FuncOp:
        return FuncOp(
            name=name,
            function_type=(input_types, return_types),
            region=Region(),
            visibility="private",
        )

    @staticmethod
    def from_region(
        name: str,
        input_types: Sequence[Attribute],
        return_types: Sequence[Attribute],
        region: Region | type[Region.DEFAULT] = Region.DEFAULT,
        visibility: StringAttr | str | None = None,
    ) -> FuncOp:
        return FuncOp(
            name=name,
            function_type=(input_types, return_types),
            region=region,
            visibility=visibility,
        )

    def replace_argument_type(
        self,
        arg: int | BlockArgument,
        new_type: Attribute,
        rewriter: Rewriter | PatternRewriter,
    ):
        """
        Replaces the type of the argument specified by arg (either the index of the arg,
        or the BlockArgument object itself) with new_type. This also takes care of updating
        the function_type attribute.
        """
        if isinstance(arg, int):
            block = self.body.blocks.first
            assert block is not None
            try:
                arg = block.args[arg]
            except IndexError:
                raise IndexError(f"Block {block} does not have argument #{arg}")

        if arg not in self.args:
            raise ValueError(f"Arg {arg} does not belong to this function")

        rewriter.replace_value_with_new_type(arg, new_type)
        self.update_function_type()

    def update_function_type(self):
        """
        Update the function_type attribute to reflect changes in the
        block argument types or return statement arguments.
        """
        # Refuse to work with external function definitions, as they don't have block args
        assert not self.is_declaration, (
            "update_function_type does not work with function declarations!"
        )
        return_op = self.get_return_op()
        return_type = self.function_type.outputs.data

        if return_op is not None:
            return_type = return_op.operand_types

        self.properties["function_type"] = FunctionType.from_lists(
            [arg.type for arg in self.args],
            return_type,
        )

    def get_return_op(self) -> ReturnOp | None:
        """
        Helper for easily retrieving the return operation of a given
        function. Returns None if it couldn't find a return op.
        """
        if self.is_declaration:
            return None
        if (last_block := self.body.blocks.last) is None:
            return None
        ret_op = last_block.last_op
        if not isinstance(ret_op, ReturnOp):
            return None
        return ret_op

    @property
    def args(self) -> tuple[BlockArgument, ...]:
        """
        A helper to quickly get access to the block arguments of the function
        """
        assert not self.is_declaration, (
            "Function declarations don't have BlockArguments!"
        )

        block = self.body.blocks.first
        assert block is not None
        return block.args

    @property
    def is_declaration(self) -> bool:
        """
        A helper to identify functions that are external declarations (have an empty
        function body)
        """
        return not self.body.blocks

name = 'func.func' class-attribute instance-attribute

body = region_def() class-attribute instance-attribute

sym_name = prop_def(SymbolNameConstraint()) class-attribute instance-attribute

function_type = prop_def(FunctionType) class-attribute instance-attribute

sym_visibility = opt_prop_def(StringAttr) class-attribute instance-attribute

arg_attrs = opt_prop_def(ArrayAttr[DictionaryAttr]) class-attribute instance-attribute

res_attrs = opt_prop_def(ArrayAttr[DictionaryAttr]) class-attribute instance-attribute

traits = traits_def(IsolatedFromAbove(), SymbolOpInterface(), FuncOpCallableInterface()) class-attribute instance-attribute

args: tuple[BlockArgument, ...] property

A helper to quickly get access to the block arguments of the function

is_declaration: bool property

A helper to identify functions that are external declarations (have an empty function body)

__init__(name: str, function_type: FunctionType | tuple[Sequence[Attribute], Sequence[Attribute]], region: Region | type[Region.DEFAULT] = Region.DEFAULT, visibility: StringAttr | str | None = None, *, arg_attrs: ArrayAttr[DictionaryAttr] | None = None, res_attrs: ArrayAttr[DictionaryAttr] | None = None)

Source code in xdsl/dialects/func.py
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
def __init__(
    self,
    name: str,
    function_type: FunctionType | tuple[Sequence[Attribute], Sequence[Attribute]],
    region: Region | type[Region.DEFAULT] = Region.DEFAULT,
    visibility: StringAttr | str | None = None,
    *,
    arg_attrs: ArrayAttr[DictionaryAttr] | None = None,
    res_attrs: ArrayAttr[DictionaryAttr] | None = None,
):
    if isinstance(visibility, str):
        visibility = StringAttr(visibility)
    if isinstance(function_type, tuple):
        inputs, outputs = function_type
        function_type = FunctionType.from_lists(inputs, outputs)
    if not isinstance(region, Region):
        region = Region(Block(arg_types=function_type.inputs))
    properties: dict[str, Attribute | None] = {
        "sym_name": StringAttr(name),
        "function_type": function_type,
        "sym_visibility": visibility,
        "arg_attrs": arg_attrs,
        "res_attrs": res_attrs,
    }
    super().__init__(properties=properties, regions=[region])

verify_() -> None

Source code in xdsl/dialects/func.py
150
151
152
153
154
155
156
157
158
159
160
161
162
def verify_(self) -> None:
    # If this is an empty region (external function), then return
    if len(self.body.blocks) == 0:
        return

    entry_block = self.body.blocks.first
    assert entry_block is not None
    block_arg_types = entry_block.arg_types
    if self.function_type.inputs.data != tuple(block_arg_types):
        raise VerifyException(
            "Expected entry block arguments to have the same types as the function "
            "input types"
        )

parse(parser: Parser) -> FuncOp classmethod

Source code in xdsl/dialects/func.py
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
@classmethod
def parse(cls, parser: Parser) -> FuncOp:
    visibility = parser.parse_optional_visibility_keyword()

    (
        name,
        input_types,
        return_types,
        region,
        extra_attrs,
        arg_attrs,
        res_attrs,
    ) = parse_func_op_like(
        parser, reserved_attr_names=("sym_name", "function_type", "sym_visibility")
    )
    func = FuncOp(
        name=name,
        function_type=(input_types, return_types),
        region=region,
        visibility=visibility,
        arg_attrs=arg_attrs,
        res_attrs=res_attrs,
    )
    if extra_attrs is not None:
        func.attributes |= extra_attrs.data
    return func

print(printer: Printer)

Source code in xdsl/dialects/func.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def print(self, printer: Printer):
    if self.sym_visibility:
        visibility = self.sym_visibility.data
        printer.print_string(" ")
        printer.print_string(visibility)

    print_func_op_like(
        printer,
        self.sym_name,
        self.function_type,
        self.body,
        self.attributes,
        arg_attrs=self.arg_attrs,
        res_attrs=self.res_attrs,
        reserved_attr_names=(
            "sym_name",
            "function_type",
            "sym_visibility",
            "arg_attrs",
        ),
    )

external(name: str, input_types: Sequence[Attribute], return_types: Sequence[Attribute]) -> FuncOp staticmethod

Source code in xdsl/dialects/func.py
213
214
215
216
217
218
219
220
221
222
@staticmethod
def external(
    name: str, input_types: Sequence[Attribute], return_types: Sequence[Attribute]
) -> FuncOp:
    return FuncOp(
        name=name,
        function_type=(input_types, return_types),
        region=Region(),
        visibility="private",
    )

from_region(name: str, input_types: Sequence[Attribute], return_types: Sequence[Attribute], region: Region | type[Region.DEFAULT] = Region.DEFAULT, visibility: StringAttr | str | None = None) -> FuncOp staticmethod

Source code in xdsl/dialects/func.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
@staticmethod
def from_region(
    name: str,
    input_types: Sequence[Attribute],
    return_types: Sequence[Attribute],
    region: Region | type[Region.DEFAULT] = Region.DEFAULT,
    visibility: StringAttr | str | None = None,
) -> FuncOp:
    return FuncOp(
        name=name,
        function_type=(input_types, return_types),
        region=region,
        visibility=visibility,
    )

replace_argument_type(arg: int | BlockArgument, new_type: Attribute, rewriter: Rewriter | PatternRewriter)

Replaces the type of the argument specified by arg (either the index of the arg, or the BlockArgument object itself) with new_type. This also takes care of updating the function_type attribute.

Source code in xdsl/dialects/func.py
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 replace_argument_type(
    self,
    arg: int | BlockArgument,
    new_type: Attribute,
    rewriter: Rewriter | PatternRewriter,
):
    """
    Replaces the type of the argument specified by arg (either the index of the arg,
    or the BlockArgument object itself) with new_type. This also takes care of updating
    the function_type attribute.
    """
    if isinstance(arg, int):
        block = self.body.blocks.first
        assert block is not None
        try:
            arg = block.args[arg]
        except IndexError:
            raise IndexError(f"Block {block} does not have argument #{arg}")

    if arg not in self.args:
        raise ValueError(f"Arg {arg} does not belong to this function")

    rewriter.replace_value_with_new_type(arg, new_type)
    self.update_function_type()

update_function_type()

Update the function_type attribute to reflect changes in the block argument types or return statement arguments.

Source code in xdsl/dialects/func.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
def update_function_type(self):
    """
    Update the function_type attribute to reflect changes in the
    block argument types or return statement arguments.
    """
    # Refuse to work with external function definitions, as they don't have block args
    assert not self.is_declaration, (
        "update_function_type does not work with function declarations!"
    )
    return_op = self.get_return_op()
    return_type = self.function_type.outputs.data

    if return_op is not None:
        return_type = return_op.operand_types

    self.properties["function_type"] = FunctionType.from_lists(
        [arg.type for arg in self.args],
        return_type,
    )

get_return_op() -> ReturnOp | None

Helper for easily retrieving the return operation of a given function. Returns None if it couldn't find a return op.

Source code in xdsl/dialects/func.py
284
285
286
287
288
289
290
291
292
293
294
295
296
def get_return_op(self) -> ReturnOp | None:
    """
    Helper for easily retrieving the return operation of a given
    function. Returns None if it couldn't find a return op.
    """
    if self.is_declaration:
        return None
    if (last_block := self.body.blocks.last) is None:
        return None
    ret_op = last_block.last_op
    if not isinstance(ret_op, ReturnOp):
        return None
    return ret_op

CallOp

Bases: IRDLOperation

Source code in xdsl/dialects/func.py
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
@irdl_op_definition
class CallOp(IRDLOperation):
    name = "func.call"
    arguments = var_operand_def()
    callee = prop_def(FlatSymbolRefAttrConstr)
    res = var_result_def()

    traits = traits_def(
        CallOpSymbolUserOpInterface(),
    )

    assembly_format = (
        "$callee `(` $arguments `)` attr-dict `:` functional-type($arguments, $res)"
    )

    def __init__(
        self,
        callee: str | SymbolRefAttr,
        arguments: Sequence[SSAValue | Operation],
        return_types: Sequence[Attribute],
    ):
        if isinstance(callee, str):
            callee = SymbolRefAttr(callee)
        super().__init__(
            operands=[arguments],
            result_types=[return_types],
            properties={"callee": callee},
        )

name = 'func.call' class-attribute instance-attribute

arguments = var_operand_def() class-attribute instance-attribute

callee = prop_def(FlatSymbolRefAttrConstr) class-attribute instance-attribute

res = var_result_def() class-attribute instance-attribute

traits = traits_def(CallOpSymbolUserOpInterface()) class-attribute instance-attribute

assembly_format = '$callee `(` $arguments `)` attr-dict `:` functional-type($arguments, $res)' class-attribute instance-attribute

__init__(callee: str | SymbolRefAttr, arguments: Sequence[SSAValue | Operation], return_types: Sequence[Attribute])

Source code in xdsl/dialects/func.py
335
336
337
338
339
340
341
342
343
344
345
346
347
def __init__(
    self,
    callee: str | SymbolRefAttr,
    arguments: Sequence[SSAValue | Operation],
    return_types: Sequence[Attribute],
):
    if isinstance(callee, str):
        callee = SymbolRefAttr(callee)
    super().__init__(
        operands=[arguments],
        result_types=[return_types],
        properties={"callee": callee},
    )

ReturnOp

Bases: IRDLOperation

Source code in xdsl/dialects/func.py
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
@irdl_op_definition
class ReturnOp(IRDLOperation):
    name = "func.return"
    arguments = var_operand_def()

    traits = traits_def(HasParent(FuncOp), IsTerminator(), ReturnLike())

    assembly_format = "attr-dict ($arguments^ `:` type($arguments))?"

    def __init__(self, *return_vals: SSAValue | Operation):
        super().__init__(operands=[return_vals])

    def verify_(self) -> None:
        func_op = self.parent_op()
        assert isinstance(func_op, FuncOp)

        function_return_types = func_op.function_type.outputs.data
        return_types = self.arguments.types
        if function_return_types != return_types:
            raise VerifyException(
                "Expected arguments to have the same types as the function output types"
            )

name = 'func.return' class-attribute instance-attribute

arguments = var_operand_def() class-attribute instance-attribute

traits = traits_def(HasParent(FuncOp), IsTerminator(), ReturnLike()) class-attribute instance-attribute

assembly_format = 'attr-dict ($arguments^ `:` type($arguments))?' class-attribute instance-attribute

__init__(*return_vals: SSAValue | Operation)

Source code in xdsl/dialects/func.py
359
360
def __init__(self, *return_vals: SSAValue | Operation):
    super().__init__(operands=[return_vals])

verify_() -> None

Source code in xdsl/dialects/func.py
362
363
364
365
366
367
368
369
370
371
def verify_(self) -> None:
    func_op = self.parent_op()
    assert isinstance(func_op, FuncOp)

    function_return_types = func_op.function_type.outputs.data
    return_types = self.arguments.types
    if function_return_types != return_types:
        raise VerifyException(
            "Expected arguments to have the same types as the function output types"
        )