Skip to content

Func

func

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

FuncOpCallableInterface dataclass

Bases: CallableOpInterface

Source code in xdsl/dialects/func.py
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
@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
@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
@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
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
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
@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
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
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
@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
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
@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
@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
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
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
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
@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 | StringAttr | SymbolRefAttr,
        arguments: Sequence[SSAValue | Operation],
        return_types: Sequence[Attribute],
    ):
        super().__init__(
            operands=[arguments],
            result_types=[return_types],
            properties={"callee": SymbolRefAttr.get(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 | StringAttr | SymbolRefAttr, arguments: Sequence[SSAValue | Operation], return_types: Sequence[Attribute])

Source code in xdsl/dialects/func.py
def __init__(
    self,
    callee: str | StringAttr | SymbolRefAttr,
    arguments: Sequence[SSAValue | Operation],
    return_types: Sequence[Attribute],
):
    super().__init__(
        operands=[arguments],
        result_types=[return_types],
        properties={"callee": SymbolRefAttr.get(callee)},
    )

ReturnOp

Bases: IRDLOperation

Source code in xdsl/dialects/func.py
@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 "
                f"types: expected {func_op.function_type.outputs}, "
                f"got {ArrayAttr(return_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
def __init__(self, *return_vals: SSAValue | Operation):
    super().__init__(operands=[return_vals])

verify_() -> None

Source code in xdsl/dialects/func.py
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 "
            f"types: expected {func_op.function_type.outputs}, "
            f"got {ArrayAttr(return_types)}"
        )