Skip to content
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

backend: (csl) printing for functions, tasks and layout related things #2647

Merged
merged 20 commits into from
May 31, 2024

Conversation

dk949
Copy link
Collaborator

@dk949 dk949 commented May 27, 2024

Printing for:

  • csl.call
  • csl.task
  • csl.func
    • Merged with csl.task
  • csl.export
  • csl.layout

Also introduces a divider between the program and layout, for ease of filechecking and splitting

dk949 added 8 commits May 27, 2024 14:55
Extracted printing logic for functions and tasks into one function.

If a task can be immediately bound (i.e. it has an ID attribute), it
will be.

Also added printing for csl.call
This part operates on the program module and prints `@export_symbol`.
This also provides the second half of the symbol exporting logic (i.e.
`@export_name` in the layout file).
Not all of these ops and types are implemented in the current printer,
but this tests for everything the old branch implemented.
Also test for control tasks
@dk949 dk949 added the backend Compiler backend in xDSL label May 27, 2024
@dk949 dk949 requested review from superlopuh, AntonLydike and n-io May 27, 2024 15:20
@dk949 dk949 self-assigned this May 27, 2024
Copy link

codecov bot commented May 27, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.68%. Comparing base (3d0acbe) to head (0174108).
Report is 12 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2647      +/-   ##
==========================================
+ Coverage   89.60%   89.68%   +0.07%     
==========================================
  Files         360      361       +1     
  Lines       46172    46319     +147     
  Branches     6974     7012      +38     
==========================================
+ Hits        41373    41540     +167     
+ Misses       3725     3709      -16     
+ Partials     1074     1070       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 53 to 61
@contextmanager
def _in_block(self, block_name: str):
self.print(f"{block_name} {{")
old_prefix = self._prefix
self._prefix += self._INDENT
yield
self._prefix = old_prefix
self.print("}")
pass
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this replace the previous descend method?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, this just wraps a set of prints in <block_name> { ... }. I could combine it with descend, but it's more for cases where we don't have an MLIR Block and need to wrap something in braces.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it only being used for comptime regions, if that is the case, maybe don't need a form this generic? Just some food for thought.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also used for layout, though I can rewrite that as descend, since layout does have an MLIR Block, then make _in_block _in_comptime instead, since there are no other CSL block types.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, descend can be used to replace all uses of _in_block, but this way felt cleaner, since it's always the same pattern:

<block-name> {
    <statements>
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would your thoughts be on:

with self._in_block("comptime") as in_comptime:
    in_comptime.print(f"@export_symbol({export_val}, {q_name});")

Then we can reuse descend in _in_block and avoid repetition there?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can instead replace descend with this instead then? I also feel that the context manager "feels" nicer, I would just argue that we should focus on one method for now.

Comment on lines 63 to 77
def _task_or_fn(
self, introducer: str, name: StringAttr, bdy: Region, ftyp: FunctionType
):
args = ", ".join(
f"{self._get_variable_name_for(arg)} : {self.mlir_type_to_csl_type(arg.type)}"
for arg in bdy.block.args
)
ret = (
"void"
if len(ftyp.outputs) == 0
else self.mlir_type_to_csl_type(ftyp.outputs.data[0])
)
self.print(f"\n{introducer} {name.data}({args}) {ret} {{")
self.descend().print_block(bdy.block)
self.print("}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to have all functions that print prefixed with print_

Comment on lines 84 to 90
def _bind_task(self, name: str, kind: csl.TaskKind, id: csl.ColorIdAttr | None):
if id is None:
return
with self._in_block("comptime"):
self.print(
f"@bind_{kind.value}_task({name}, @get_{kind.value}_task_id({self._wrapp_task_id(kind, id.value.data)}));"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, something like print_task_bind

Comment on lines 122 to 130
def _export_sym_constness(self, ty: FunctionType | csl.PtrType):
if isinstance(ty, FunctionType):
return None
match ty.constness.data:
case csl.PtrConst.VAR:
return True
case csl.PtrConst.CONST:
return False

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand what this function does. Type annotations and docstrings would go a long way in making this more reviewable!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When exporting symbols we have to specify if they are host-writable, except for functions, where it's a compile error to do so.

Will add a docstring and return type

Comment on lines 63 to 65
def _task_or_fn(
self, introducer: str, name: StringAttr, bdy: Region, ftyp: FunctionType
):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introducer could benefit from a rename and better type annotation (e.g. kind: Literal['task', 'fn'])

)


def _get_layout_program(ops: BlockOps) -> tuple[csl.CslModuleOp, csl.CslModuleOp]:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be more appropriate to pass the module here instead of the iterable of it's ops?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, docstrings!

@dk949
Copy link
Collaborator Author

dk949 commented May 29, 2024

@AntonLydike for the print_ prefix, should these still be private (i.e. _print_)?

Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is overall a pretty good first printer PR, although I would really really like to see more docstrings here!

@AntonLydike
Copy link
Collaborator

@AntonLydike for the print_ prefix, should these still be private (i.e. _print_)?

I think they can easily be private as well!

Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better already!

To be used in a `with` statement.
Surrounds everything in the `with` with curly braces and .

This is used instead of descend when
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

premature end of docstring, I think

self, introducer: str, name: StringAttr, bdy: Region, ftyp: FunctionType
def _print_task_or_fn(
self,
kind: Literal["fn"] | Literal["task"],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified to Literal["fn", "task"]

self.descend().print_block(bdy.block)
self.print("}")

def _wrapp_task_id(self, kind: csl.TaskKind, id: int) -> str:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: typo - should be wrap_task_id

Comment on lines -278 to +427
for mod in prog.body.block.ops:
assert isinstance(mod, csl.CslModuleOp)
ctx.print_block(mod.body.block)
layout, program = _get_layout_program(prog)
ctx.print_block(program.body.block)
ctx.print(ctx.DIVIDER)
ctx.print_block(layout.body.block)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the previous approach more of iterating over the modules, I feel this provides more flexibility for e.g. multiple programs or multiple layouts. This feels overly restrictive

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to print program first as that's when we collect symbols to be exported.

We can allow for multiple programs, but there still needs to be some logic to make sure they all get processed before the one layout.

case anyop:
self.print(f"unknown op {anyop}", prefix="//")

def descend(self) -> CslPrintContext:
@contextmanager
def descend(self, block_start: str = ""):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can add a type annotation here so that we know the type of the context manager:

from typing import ContextManager
# ...
@contextmanager
def descend(self, block_start: str = "") ->  ContextManager[CslPrintContext]:
    ....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious whether this will work, I would have expected it to rather be -> Iterator[CslPrintContext]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iirc pyright (at least when I tested it) were happy with both, but I find ContextManager to be the most correct imo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I try ContextManager[CslPrintContext], pyright produces all sorts of errors. ruff also complains that typing.ContextManager is deprecated in favor of AbstractContextManager, AbstractContextManager[CslPrintContext] causes all the same errors, since it's not compatible with Generator.

Iterator[CslPrintContext] works, as does Generator[CslPrintContext, None, None], but I think both of these make the function signature more confusing. Personally, I would leave the return type auto-deduced, and rely on the docstring

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the type annotation is for the function passed into the decorator, so ContextManager is incorrect

Copy link
Collaborator

@AntonLydike AntonLydike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is in a good-to-go state now!

@dk949
Copy link
Collaborator Author

dk949 commented May 30, 2024

I think this is in a good-to-go state now!

I forgot to remove the old _in_block method, but I recon it's good now.

@AntonLydike AntonLydike merged commit 6cfcb63 into main May 31, 2024
10 checks passed
@AntonLydike AntonLydike deleted the david/csl-backend/functions+layout branch May 31, 2024 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend Compiler backend in xDSL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants