Skip to content

feat: add debug dialect #342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 31, 2025
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,4 @@ main.py
.ruff_cache
.python-version
!package.json
!src/kirin/dialects/debug
70 changes: 70 additions & 0 deletions src/kirin/dialects/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import ast

import rich

from kirin import ir, decl, types, interp, lowering, exceptions

dialect = ir.Dialect("debug")


class InfoLowering(ir.FromPythonCall):

def lower(
self, stmt: type, state: lowering.LoweringState, node: ast.Call
) -> lowering.Result:
if len(node.args) == 0:
raise exceptions.DialectLoweringError(
"info() requires at least one argument"
)

msg = state.visit(node.args[0]).expect_one()
if len(node.args) > 1:
inputs = tuple(state.visit(arg).expect_one() for arg in node.args[1:])
else:
inputs = ()
return lowering.Result(state.append_stmt(Info(msg=msg, inputs=inputs)))


@decl.statement(dialect=dialect)
class Info(ir.Statement):
"""print debug information.

This statement is used to print debug information during
execution. The compiler has freedom to choose how to print
the information and send it back to the caller. Note that
in the case of heterogeneous hardware, this may not be printed
on the same device as the caller but instead being a log.
"""

traits = frozenset({InfoLowering()})
msg: ir.SSAValue = decl.info.argument(types.String)
inputs: tuple[ir.SSAValue, ...] = decl.info.argument()


@lowering.wraps(Info)
def info(msg: str, *inputs) -> None: ...


@dialect.register(key="main")
class ConcreteMethods(interp.MethodTable):

@interp.impl(Info)
def info(self, interp: interp.Interpreter, frame: interp.Frame, stmt: Info):
# print("INFO:", frame.get(stmt.msg))
rich.print(
"[dim]┌───────────────────────────────────────────────────────────────[/dim]"
)
rich.print("[dim]│[/dim] [bold cyan]INFO:[/bold cyan] ", end="", sep="")
print(frame.get(stmt.msg))
for input in stmt.inputs:
rich.print(
"[dim]│[/dim] ",
input.name or "unknown",
"[dim] = [/dim]",
end="",
sep="",
)
print(frame.get(input))
rich.print(
"[dim]└───────────────────────────────────────────────────────────────[/dim]"
)
18 changes: 18 additions & 0 deletions test/dialects/test_debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from kirin.prelude import basic_no_opt
from kirin.dialects import debug


def test_debug_printing():
@basic_no_opt.add(debug)
def test_if_inside_for() -> int:
count = 0
for i in range(5):
count = count + 1
something_else = count + 2
debug.info("current count before", count, something_else)
if True:
count = count + 100
debug.info("inside the ifelse", count, something_else)
else:
count = count + 300
return count