Skip to content

Warning

This page is under construction. The content may be incomplete or incorrect. Submit an issue on GitHub if you need help or want to contribute.

Dialects for special Python functions

There are some special built-in Python functions that does not necessarily provide new data types when using them as Kirin dialects.

For example, the py.range dialect may not use a Python range type, the actual type can be decided by another dialect that implements the type inference method for Range statement, e.g ilist dialect provides an IList implementation for Range statement.

The reason for this is that in many cases, eDSLs are not interested in the actual data type of the result, but rather the semantics of the operation. For ilist dialect, the Range statement is just a syntax sugar for creating a list of integers. The compiler will decide what the actual implementation (such as the memory layout) of the list should be.

References

Iterable

kirin.dialects.py.iterable

This module provides access to Python iterables.

This is used to lower Python loops into cf dialect.

This module contains the common methods for the Python iterable:

  • The Iter statement class.
  • The Next statement class.
  • The lowering pass for the iterable.
  • The concrete implementation of the iterable.

This dialect maps iter() and next() calls to the Iter and Next statements.

PyRangeIterType module-attribute

PyRangeIterType = PyClass(type(iter(range(0))))

dialect module-attribute

dialect = Dialect('py.iterable')

Concrete

Bases: MethodTable

iter_

iter_(interp, frame: interp.Frame, stmt: Iter)
Source code in src/kirin/dialects/py/iterable.py
45
46
47
@interp.impl(Iter)
def iter_(self, interp, frame: interp.Frame, stmt: Iter):
    return (iter(frame.get(stmt.value)),)

next_

next_(interp, frame: interp.Frame, stmt: Next)
Source code in src/kirin/dialects/py/iterable.py
49
50
51
@interp.impl(Next)
def next_(self, interp, frame: interp.Frame, stmt: Next):
    return (next(frame.get(stmt.iter), None),)

Iter kirin-statement

Iter(value: ir.SSAValue)

Bases: Statement

This is equivalent to iter(value) in Python.

iter kirin-result

iter: ResultValue = result(Any)

traits class-attribute instance-attribute

traits = frozenset({})

value kirin-argument

value: SSAValue = argument(Any)

Lowering dataclass

Lowering()

Bases: FromPythonAST

lower_Call_iter

lower_Call_iter(
    state: lowering.State, node: Call
) -> lowering.Result
Source code in src/kirin/dialects/py/iterable.py
69
70
71
72
73
74
75
@lowering.akin(iter)
def lower_Call_iter(self, state: lowering.State, node: Call) -> lowering.Result:
    if len(node.args) != 1:
        raise lowering.BuildError("iter() takes exactly 1 argument")
    return state.current_frame.push(
        Iter(state.lower(node.args[0]).expect_one()),
    )

lower_Call_next

lower_Call_next(
    state: lowering.State, node: Call
) -> lowering.Result
Source code in src/kirin/dialects/py/iterable.py
77
78
79
80
81
82
83
84
85
86
87
88
@lowering.akin(next)
def lower_Call_next(self, state: lowering.State, node: Call) -> lowering.Result:
    if len(node.args) == 2:
        raise lowering.BuildError(
            "next() does not throw StopIteration inside kernel"
        )
    if len(node.args) != 1:
        raise lowering.BuildError("next() takes exactly 1 argument")

    return state.current_frame.push(
        Next(state.lower(node.args[0]).expect_one()),
    )

Next kirin-statement

Next(iter: ir.SSAValue)

Bases: Statement

This is equivalent to next(iterable, None) in Python.

iter kirin-argument

iter: SSAValue = argument(Any)

value kirin-result

value: ResultValue = result(Any)

TypeInfer

Bases: MethodTable

iter_

iter_(interp, frame: interp.Frame, stmt: Iter)
Source code in src/kirin/dialects/py/iterable.py
57
58
59
@interp.impl(Iter, types.PyClass(range))
def iter_(self, interp, frame: interp.Frame, stmt: Iter):
    return (PyRangeIterType,)

next_

next_(interp, frame: interp.Frame, stmt: Next)
Source code in src/kirin/dialects/py/iterable.py
61
62
63
@interp.impl(Next, PyRangeIterType)
def next_(self, interp, frame: interp.Frame, stmt: Next):
    return (types.Int,)

Len

kirin.dialects.py.len

The Len dialect.

This dialect maps the len() call to the Len statement:

  • The Len statement class.
  • The lowering pass for the len() call.
  • The concrete implementation of the len() call.

dialect module-attribute

dialect = Dialect('py.len')

Concrete

Bases: MethodTable

len

len(interp, frame: interp.Frame, stmt: Len)
Source code in src/kirin/dialects/py/len.py
30
31
32
@interp.impl(Len)
def len(self, interp, frame: interp.Frame, stmt: Len):
    return (len(frame.get(stmt.value)),)

ConstProp

Bases: MethodTable

len

len(interp, frame: interp.Frame, stmt: Len)
Source code in src/kirin/dialects/py/len.py
38
39
40
41
42
43
44
45
46
@interp.impl(Len)
def len(self, interp, frame: interp.Frame, stmt: Len):
    value = frame.get(stmt.value)
    if isinstance(value, const.Value):
        return (const.Value(len(value.data)),)
    elif isinstance(value, const.PartialTuple):
        return (const.Value(len(value.data)),)
    else:
        return (const.Result.top(),)

Len kirin-statement

Len(value: ir.SSAValue)

Bases: Statement

name class-attribute instance-attribute

name = 'len'

result kirin-result

result: ResultValue = result(Int)

traits class-attribute instance-attribute

traits = frozenset({Pure(), FromPythonCall()})

value kirin-argument

value: SSAValue = argument(Any)

Lowering dataclass

Lowering()

Bases: FromPythonAST

lower_Call_len

lower_Call_len(
    state: lowering.State, node: ast.Call
) -> lowering.Result
Source code in src/kirin/dialects/py/len.py
52
53
54
@lowering.akin(len)
def lower_Call_len(self, state: lowering.State, node: ast.Call) -> lowering.Result:
    return state.current_frame.push(Len(state.lower(node.args[0]).expect_one()))

Range

kirin.dialects.py.range

The range dialect for Python.

This dialect models the builtin range() function in Python.

The dialect includes: - The Range statement class. - The lowering pass for the range() function.

This dialect does not include a concrete implementation or type inference for the range() function. One needs to use other dialect for the concrete implementation and type inference, e.g., ilist dialect.

dialect module-attribute

dialect = Dialect('py.range')

Range kirin-statement

Range(
    start: ir.SSAValue, stop: ir.SSAValue, step: ir.SSAValue
)

Bases: Statement

name class-attribute instance-attribute

name = 'range'

result kirin-result

result: ResultValue = result(PyClass(range))

start kirin-argument

start: SSAValue = argument(Int)

step kirin-argument

step: SSAValue = argument(Int)

stop kirin-argument

stop: SSAValue = argument(Int)

traits class-attribute instance-attribute

traits = frozenset({Pure(), FromPythonRangeLike()})

TypeInfer

Bases: MethodTable

eltype_range

eltype_range(
    interp_, frame: interp.Frame, stmt: eltype.ElType
)
Source code in src/kirin/dialects/py/range.py
34
35
36
@interp.impl(eltype.ElType, types.PyClass(range))
def eltype_range(self, interp_, frame: interp.Frame, stmt: eltype.ElType):
    return (types.Int,)

Slice

kirin.dialects.py.slice

The slice dialect for Python.

This dialect provides a Slice statement that represents a slice object in Python:

  • The Slice statement class.
  • The lowering pass for the slice call.
  • The concrete implementation of the slice call.
  • The type inference implementation of the slice call.

T module-attribute

T = TypeVar('T')

dialect module-attribute

dialect = Dialect('py.slice')

Concrete

Bases: MethodTable

_slice

_slice(interp, frame: interp.Frame, stmt: Slice)
Source code in src/kirin/dialects/py/slice.py
 96
 97
 98
 99
100
101
102
103
104
@interp.impl(Slice)
def _slice(self, interp, frame: interp.Frame, stmt: Slice):
    start, stop, step = frame.get_values(stmt.args)
    if start is None and step is None:
        return (SliceAttribute(None, stop, None),)
    elif step is None:
        return (SliceAttribute(start, stop, None),)
    else:
        return (SliceAttribute(start, stop, step),)

Lowering dataclass

Lowering()

Bases: FromPythonAST

lower_Call_slice

lower_Call_slice(
    state: lowering.State, node: ast.Call
) -> lowering.Result
Source code in src/kirin/dialects/py/slice.py
123
124
125
126
127
@lowering.akin(slice)
def lower_Call_slice(
    self, state: lowering.State, node: ast.Call
) -> lowering.Result:
    return _lower_slice(state, node)

lower_Slice

lower_Slice(
    state: lowering.State, node: ast.Slice
) -> lowering.Result
Source code in src/kirin/dialects/py/slice.py
110
111
112
113
114
115
116
117
118
119
120
121
def lower_Slice(self, state: lowering.State, node: ast.Slice) -> lowering.Result:
    def value_or_none(expr: ast.expr | None) -> ir.SSAValue:
        if expr is not None:
            return state.lower(expr).expect_one()
        else:
            return state.current_frame.push(Constant(None)).result

    lower = value_or_none(node.lower)
    upper = value_or_none(node.upper)
    step = value_or_none(node.step)

    return state.current_frame.push(Slice(start=lower, stop=upper, step=step))

Slice kirin-statement

Slice(
    start: ir.SSAValue, stop: ir.SSAValue, step: ir.SSAValue
)

Bases: Statement

Source code in src/kirin/dialects/py/slice.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def __init__(
    self, start: ir.SSAValue, stop: ir.SSAValue, step: ir.SSAValue
) -> None:
    if not (
        isinstance(stop.type, types.TypeAttribute)
        and isinstance(start.type, types.TypeAttribute)
    ):
        result_type = types.Bottom
    elif start.type.is_subseteq(types.NoneType):
        if stop.type.is_subseteq(types.NoneType):
            result_type = types.Bottom
        else:
            result_type = types.Slice[stop.type]
    else:
        result_type = types.Slice[start.type]

    super().__init__(
        args=(start, stop, step),
        result_types=[result_type],
        args_slice={"start": 0, "stop": 1, "step": 2},
    )

name class-attribute instance-attribute

name = 'slice'

result kirin-result

result: ResultValue = result(Slice[T])

start kirin-argument

start: SSAValue = argument(T | NoneType)

step kirin-argument

step: SSAValue = argument(T | NoneType)

stop kirin-argument

stop: SSAValue = argument(T | NoneType)

traits class-attribute instance-attribute

traits = frozenset({Pure(), SliceLowering()})

SliceAttribute dataclass

SliceAttribute(
    start: int | None, stop: int | None, step: int | None
)

Bases: Data[slice]

start instance-attribute

start: int | None

step instance-attribute

step: int | None

stop instance-attribute

stop: int | None

__hash__

__hash__()
Source code in src/kirin/dialects/py/slice.py
86
87
def __hash__(self):
    return hash((type(self), self.start, self.stop, self.step))

__post_init__

__post_init__() -> None
Source code in src/kirin/dialects/py/slice.py
73
74
75
76
77
78
79
80
81
def __post_init__(self) -> None:
    if self.start is None and self.step is None:
        self.type = types.Slice[types.Literal(self.stop)]
    else:
        self.type = types.Slice3[
            types.Literal(self.start),
            types.Literal(self.stop),
            types.Literal(self.step),
        ]

print_impl

print_impl(printer: Printer) -> None
Source code in src/kirin/dialects/py/slice.py
89
90
def print_impl(self, printer: Printer) -> None:
    return printer.plain_print(f"slice({self.start}, {self.stop}, {self.step})")

unwrap

unwrap()

Returns the underlying data value.

Source code in src/kirin/dialects/py/slice.py
83
84
def unwrap(self):
    return slice(self.start, self.stop, self.step)

SliceLowering dataclass

SliceLowering()

Bases: FromPythonCall['Slice']

lower

lower(
    stmt: type[Slice], state: lowering.State, node: ast.Call
) -> lowering.Result
Source code in src/kirin/dialects/py/slice.py
25
26
27
28
def lower(
    self, stmt: type["Slice"], state: lowering.State, node: ast.Call
) -> lowering.Result:
    return _lower_slice(state, node)

_lower_slice

_lower_slice(
    state: lowering.State, node: ast.Call
) -> lowering.Result
Source code in src/kirin/dialects/py/slice.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def _lower_slice(state: lowering.State, node: ast.Call) -> lowering.Result:
    if len(node.args) == 1:
        start = state.lower(ast.Constant(None)).expect_one()
        stop = state.lower(node.args[0]).expect_one()
        step = state.lower(ast.Constant(None)).expect_one()
    elif len(node.args) == 2:
        start = state.lower(node.args[0]).expect_one()
        stop = state.lower(node.args[1]).expect_one()
        step = state.lower(ast.Constant(None)).expect_one()
    elif len(node.args) == 3:
        start = state.lower(node.args[0]).expect_one()
        stop = state.lower(node.args[1]).expect_one()
        step = state.lower(node.args[2]).expect_one()
    else:
        raise lowering.BuildError("slice() takes 1-3 arguments")

    return state.current_frame.push(Slice(start, stop, step))

Built-in Function

kirin.dialects.py.builtin

builtin dialect for python builtins

This dialect provides implementations for builtin functions like abs and sum.

  • Statements: Abs, Sum.
  • The lowering pass for the builtin functions.
  • The concrete implementation of the builtin functions.
  • The type inference implementation of the builtin functions.

This dialect maps ast.Call nodes of builtin functions to the Abs and Sum statements.

T module-attribute

T = TypeVar('T', bound=Int | Float)

dialect module-attribute

dialect = Dialect('py.builtin')

Abs kirin-statement

Abs(value: ir.SSAValue)

Bases: Statement

name class-attribute instance-attribute

name = 'abs'

result kirin-result

result: ResultValue = result(T)

traits class-attribute instance-attribute

traits = frozenset({Pure(), FromPythonCall()})

value kirin-argument

value: SSAValue = argument(T, print=False)

Concrete

Bases: MethodTable

_sum

_sum(interp, frame: interp.Frame, stmt: Sum)
Source code in src/kirin/dialects/py/builtin.py
58
59
60
@interp.impl(Sum)
def _sum(self, interp, frame: interp.Frame, stmt: Sum):
    return (sum(frame.get(stmt.value)),)

abs

abs(interp, frame: interp.Frame, stmt: Abs)
Source code in src/kirin/dialects/py/builtin.py
54
55
56
@interp.impl(Abs)
def abs(self, interp, frame: interp.Frame, stmt: Abs):
    return (abs(frame.get(stmt.value)),)

Lowering dataclass

Lowering()

Bases: FromPythonAST

lower_Call_abs

lower_Call_abs(
    state: lowering.State, node: Call
) -> lowering.Result
Source code in src/kirin/dialects/py/builtin.py
42
43
44
@lowering.akin(abs)
def lower_Call_abs(self, state: lowering.State, node: Call) -> lowering.Result:
    return state.current_frame.push(Abs(state.lower(node.args[0]).expect_one()))

lower_Call_sum

lower_Call_sum(
    state: lowering.State, node: Call
) -> lowering.Result
Source code in src/kirin/dialects/py/builtin.py
46
47
48
@lowering.akin(sum)
def lower_Call_sum(self, state: lowering.State, node: Call) -> lowering.Result:
    return state.current_frame.push(Sum(state.lower(node.args[0]).expect_one()))

Sum kirin-statement

Sum(value: ir.SSAValue)

Bases: Statement

name class-attribute instance-attribute

name = 'sum'

result kirin-result

result: ResultValue = result(Any)

traits class-attribute instance-attribute

traits = frozenset({Pure(), FromPythonCall()})

value kirin-argument

value: SSAValue = argument(Any, print=False)

TypeInfer

Bases: MethodTable

absf

absf(interp, frame, stmt)
Source code in src/kirin/dialects/py/builtin.py
70
71
72
@interp.impl(Abs, types.Float)
def absf(self, interp, frame, stmt):
    return (types.Float,)

absi

absi(interp, frame, stmt)
Source code in src/kirin/dialects/py/builtin.py
66
67
68
@interp.impl(Abs, types.Int)
def absi(self, interp, frame, stmt):
    return (types.Int,)