Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/kirin/dialects/ilist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
ForEach as ForEach,
IListType as IListType,
)
from .passes import IListDesugar as IListDesugar
from .passes import IListToLoop as IListToLoop, IListDesugar as IListDesugar
from .runtime import IList as IList
from ._dialect import dialect as dialect
from ._wrapper import ( # careful this is not the builtin range
Expand Down
24 changes: 19 additions & 5 deletions src/kirin/dialects/ilist/interp.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import typing
from collections.abc import Iterable

from kirin import ir, types
from kirin.interp import Frame, Interpreter, MethodTable, impl
from kirin.dialects.py.len import Len
Expand Down Expand Up @@ -67,25 +70,36 @@ def scan(self, interp: Interpreter, frame: Frame, stmt: Scan):
return ((carry, IList(ys, types.Any)),)

@impl(Foldr)
def foldr(self, interp: Interpreter, frame: Frame, stmt: Foldr):
def foldr(
self, interp: Interpreter, frame: Frame, stmt: Foldr
) -> tuple[typing.Any]:
return self.fold(
interp, frame, stmt, reversed(frame.get_casted(stmt.collection, IList).data)
)

@impl(Foldl)
def foldl(self, interp: Interpreter, frame: Frame, stmt: Foldl):
def foldl(
self, interp: Interpreter, frame: Frame, stmt: Foldl
) -> tuple[typing.Any]:
return self.fold(
interp, frame, stmt, frame.get_casted(stmt.collection, IList).data
)

def fold(self, interp: Interpreter, frame: Frame, stmt: Foldr | Foldl, coll):
fn: ir.Method = frame.get(stmt.fn)
def fold(
self,
interp: Interpreter,
frame: Frame,
stmt: Foldr | Foldl,
coll: Iterable[typing.Any],
) -> tuple[typing.Any]:
fn: ir.Method[..., typing.Any] = frame.get(stmt.fn)
init = frame.get(stmt.init)

acc = init
for elem in coll:
# NOTE: assume fn has been type checked
_, acc = interp.call(fn.code, fn, acc, elem)
inputs = (elem, acc) if isinstance(stmt, Foldr) else (acc, elem)
_, acc = interp.call(fn.code, fn, *inputs)
return (acc,)

@impl(ForEach)
Expand Down
60 changes: 57 additions & 3 deletions src/kirin/dialects/ilist/passes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
from typing import Any
from dataclasses import field, dataclass

from kirin import ir, types
from kirin.rewrite import Walk, Chain, Fixpoint
from kirin.passes.abc import Pass
from kirin.rewrite.abc import RewriteResult
from kirin.dialects.ilist.rewrite import List2IList, ConstList2IList
from kirin.rewrite.abc import RewriteRule, RewriteResult
from kirin.passes.typeinfer import TypeInfer
from kirin.dialects.ilist.rewrite import (
List2IList,
AllToForLoop,
AnyToForLoop,
MapToForLoop,
FoldToForLoop,
ScanToForLoop,
ConstList2IList,
ForEachToForLoop,
)


class IListDesugar(Pass):
Expand All @@ -11,12 +24,53 @@ class IListDesugar(Pass):
constant `list` type into `IList` type.
"""

def unsafe_run(self, mt: ir.Method) -> RewriteResult:
def unsafe_run(self, mt: ir.Method[..., Any]) -> RewriteResult:
for arg in mt.args:
_check_list(arg.type, arg.type)
return Fixpoint(Walk(Chain(ConstList2IList(), List2IList()))).rewrite(mt.code)


@dataclass
class IListToLoop(Pass):
"""Lower IList combinators to indexed SCF loops within one method.

Handles Map, Foldl, Foldr, Scan, ForEach, Any and All. The result retains
callback calls, IList construction and concatenation, integer indexing,
ranges, tuples, and arithmetic. Separate callee bodies are unchanged.

Requires current inferred types. For methods not marked inferred, runs
type inference first. Rules leave unsupported type representations unchanged;
callers requiring complete normalization must check for residual combinators.

The dialect group must include scf, func, ilist, py.constant, py.len,
py.indexing, py.binop, py.boolop, and py.tuple.
"""

typeinfer: TypeInfer = field(init=False)
rule: RewriteRule = field(init=False)

def __post_init__(self) -> None:
self.typeinfer = TypeInfer(self.dialects, no_raise=self.no_raise)
self.rule = Fixpoint(
Walk(
Chain(
MapToForLoop(),
FoldToForLoop(),
ScanToForLoop(),
ForEachToForLoop(),
AnyToForLoop(),
AllToForLoop(),
)
)
)

def unsafe_run(self, mt: ir.Method[..., Any]) -> RewriteResult:
result = RewriteResult()
if not mt.inferred:
result = self.typeinfer.unsafe_run(mt)
return self.rule.rewrite(mt.code).join(result)


def _check_list(total: types.TypeAttribute, type_: types.TypeAttribute):
if isinstance(type_, types.Generic):
_check_list(total, type_.body)
Expand Down
8 changes: 8 additions & 0 deletions src/kirin/dialects/ilist/rewrite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from .list import List2IList as List2IList
from .const import ConstList2IList as ConstList2IList
from .unroll import Unroll as Unroll
from .to_loop import (
AllToForLoop as AllToForLoop,
AnyToForLoop as AnyToForLoop,
MapToForLoop as MapToForLoop,
FoldToForLoop as FoldToForLoop,
ScanToForLoop as ScanToForLoop,
ForEachToForLoop as ForEachToForLoop,
)
from .hint_len import HintLen as HintLen
from .flatten_add import FlattenAdd as FlattenAdd
from .to_range_loop import ToRangeFor as ToRangeFor
Expand Down
Loading