Skip to content

Kirin

Kirin is the Kernel Intermediate Representation Infrastructure. It is a compiler infrastructure for building compilers for domain-specific languages (DSLs) that target scientific computing kernels.

Features

  • MLIR-like dialects as composable python packages
  • Generated Python frontend for your DSLs
  • Pythonic API for building compiler passes
  • Julia-like abstract interpretation framework
  • Builtin support for interpretation
  • Builtin support Python type system and type inference
  • Type hinted via modern Python type hints

Kirin's mission

Compiler toolchain for scientists. Scientists are building domain-specific languages (DSLs) for scientific purposes. These DSLs are often high-level, and their instructions are usually slower than the low-level instructions and thus result in smaller programs. The performance does not need to be as good as a native program, but scientists want good interactivity and fast prototyping. Most importantly, scientists usually just want to write Python as their frontend.

Quick Example: the beer language

In this example, we will mutate python's semantics to support a small eDSL (embedded domain-specific language) called beer. It describes the process of brewing beer and get drunk.

First, let's define the dialect object, which is a registry for all the objects modeling the semantics.

from kirin import ir

dialect = ir.Dialect("beer")

Next, we want to define a runtime value Beer for the beer language so that we may use later in our interpreter.

from dataclasses import dataclass

@dataclass
class Beer:
    brand: str

Now, we can define the beer language's statements.

from kirin.decl import statement, info
from kirin.dialects.py import types

@statement(dialect=dialect)
class NewBeer(Statement):
    name = "new_beer"
    traits = frozenset({ir.Pure()})
    brand: ir.SSAValue = info.argument(types.String)
    result: ir.ResultValue = info.result(types.PyClass(Beer))

the NewBeer statement creates a new beer object with a given brand. Thus it takes a string as an argument and returns a Beer object. The name field specifies the name of the statement in the IR text format (e.g printing). The traits field specifies the statement's traits, in this case, it is a pure function because each brand name uniquely identifies a beer object.

@statement(dialect=dialect)
class Drink(Statement):
    name = "drink"
    beverage: SSAValue = info.argument(types.PyClass(Beer))


@statement(dialect=dialect)
class Pour(Statement):
    name = "pour"
    beverage: SSAValue = info.argument(types.PyClass(Beer))
    amount: SSAValue = info.argument(types.Int)


@statement(dialect=dialect)
class RandomBranch(Statement):
    name = "random_br"
    traits = frozenset({IsTerminator()})
    cond: SSAValue = info.argument(types.Bool)
    then_arguments: tuple[SSAValue, ...] = info.argument()
    else_arguments: tuple[SSAValue, ...] = info.argument()
    then_successor: Block = info.block()
    else_successor: Block = info.block()


@statement(dialect=dialect)
class Puke(Statement):
    name = "puke"