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

[Calyx] Add Invoke operation. #1679

Closed
Tracked by #1690
cgyurgyik opened this issue Aug 31, 2021 · 19 comments
Closed
Tracked by #1690

[Calyx] Add Invoke operation. #1679

cgyurgyik opened this issue Aug 31, 2021 · 19 comments
Labels
Calyx The Calyx dialect good first issue Good for newcomers

Comments

@cgyurgyik
Copy link
Member

cgyurgyik commented Aug 31, 2021

I think this will be useful for lowering any multi-component designs.

@cgyurgyik cgyurgyik added the Calyx The Calyx dialect label Aug 31, 2021
@rachitnigam
Copy link
Contributor

For reference, here is the PR in the Calyx repo that implemented invoke: calyxir/calyx#300. In addition to multi-component designs, pipelines in Calyx can be encoded using invoke.

@rachitnigam
Copy link
Contributor

A relevant change that'll show up soon in the Calyx native tree is this: calyxir/calyx#633. Since the invoke-with syntax is going to be optional (which means bare invoke will be valid), this change is an enhancement instead of a breaking change.

@cgyurgyik
Copy link
Member Author

Just some thought on this: with Invoke, we invoke instances of components. e.g.

myComp = Comp();
...
invoke myComp(in=1'd1)();

In MLIR, we represent instance names as string attributes, which makes it messy to verify that exists. A cleaner option might be to instead use symbols for instance names, with the table living in ComponentOp.

@rachitnigam
Copy link
Contributor

The invoke-with change has been in merged: calyxir/calyx#712

@rachitnigam
Copy link
Contributor

Invoke's also support the new ref syntax. These changes can be added iteratively into the dialect

@linuxlonelyeagle
Copy link
Member

@rachitnigam I haven't been studying mlir for long,but i really like mlir,i would love to contribute something to mlir learn more about mlir,i also hope to make some progress with compilers and computer architectures.

@linuxlonelyeagle
Copy link
Member

@rachitnigam Can you help me with this problem? I'm working on a calyx dialect multi-component design and I'm researching what kind of IR should be generated for the multi-component design.Thanks!

@linuxlonelyeagle
Copy link
Member

@rachitnigam Recently I've been thinking about how to implement invoke. Here are my initial thoughts.

  • First define the invoke op
    The following uses the invoke method.
%comp.in = calyx.instance @comp of @myComp
%c1 = hw.constant 1 : i1
...
calyx.invoke @comp(%comp.in= %c1) // I'm not sure if this is the right style? Is there a better idea?
// Or the following form
calyx.invoke @comp(in = %c1)
  • The following is about pass
    I'm going to write the pass of calyx.invoke to lower-calyx-to-fsm(It looks like lower calyx must be used first with lower-calyx-to-fsm).The specific implementation of pass is to replace calyx.invoke with calyx.group, then add it to calyx.wires, then use calyx.enable to call calyx.group, then use the method of lower calyx in pass to lower to fsm.

@rachitnigam
Copy link
Contributor

Seems reasonable to me. For what its worth, in the native compiler, we have a separate lowering pass called compile-invoke that is responsible for transforming invoke into a group. We do this before the equivalent of the calyx-to-fsm pass so that other optimizations that do not support invoke can perform their optimizations before lowering.

@linuxlonelyeagle
Copy link
Member

Seems reasonable to me. For what its worth, in the native compiler, we have a separate lowering pass called compile-invoke that is responsible for transforming invoke into a group. We do this before the equivalent of the calyx-to-fsm pass so that other optimizations that do not support invoke can perform their optimizations before lowering.

I will try to finish it next.

@linuxlonelyeagle
Copy link
Member

linuxlonelyeagle commented Jun 15, 2023

I am trying to set the style of invoke and finally I think this should be better.

%comp.in = calyx.instance @comp of @myComp : i32 
%c10 = hw.constant 10 : i32
...
// -> () is used to indicate the type of the parameter and the port, which should be the same.
calyx.invoke @comp(%comp.in = %c10) -> (i32)

Here are some of my reasons.

  • The port is specified in the form of ssa value(%comp.in).
    This will allow mlir to detect if the port is available. Because the port is obtained through calyx.instance, it means that the port exists in the component, avoiding repeated detection (calyx.instance should have code to detect port information).
    If you do not use ssa value, you may need to recheck the port information of the component.
  • The semantics are clearer.
    Although I think it lowers the level of abstraction.
  • It should be easier to write the pass later.

@linuxlonelyeagle
Copy link
Member

Invoke's also support the new ref syntax. These changes can be added iteratively into the dialect

This is something I'm not quite sure what needs to be done yet, but I think changes need to be made in other operations.

@rachitnigam
Copy link
Contributor

Here is an example that shows how they work: https://docs.calyxir.org/lang/memories-by-reference.html

@linuxlonelyeagle
Copy link
Member

Hello, I seem to have encountered a bit of a problem, I intend to implement the simple invoke part first (the ref part involves the modification of other operations). Here is the problem I have encountered and I would like to have some discussion with you.

  • A group must have group.done. but it doesn't seem to be well represented in the invoke.
    As an example, for example, invoke @id(...) -> (...) , Should calyx.group_done = %id.done be generated as a terminator in the group here(Since the component we are using is %id, the last terminator in the group is calyx.group_done = %id.done, this is what I intend to do in the current implementation).We must also verify that the components of the invoke are sequence circuits?This is the only way to ensure that there are out ports in the component and that the group_done operation can be successfully created in the group.
  • I'm a little confused about what the semantics of invoke actually are.
    For example.
calyx.invoke @id(%id.in = %c1_10, %other.in = %c10) -> (i32, i32) // The port used here can be the port of another instance.

At first I thought the port used in the invoke had to be the port of the calling component.

@linuxlonelyeagle
Copy link
Member

One more question, I found the following code.

  hw.module.extern @params<WIDTH: i32>(%in: !hw.int<#hw.param.decl.ref<"WIDTH">>) -> (out: !hw.int<#hw.param.decl.ref<"WIDTH">>) attributes {filename = "test.v"}
  calyx.component @A(%in_0: i32, %in_1: i32, %go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%out_0: i32, %out_1: i32, %done: i1 {done}) {
    %c1_1 = hw.constant 1 : i1
    %params.in, %params.out = calyx.primitive @params_0 of @params<WIDTH: i32 = 32> : i32, i32
    %prim.in, %prim.out = calyx.primitive @prim_0 of @prim : i32, i32

I think calyx.invoke should be changed accordingly (compared to the original calyx project), I looked up calyx operations, libOps and calyx.component defined instances are able to find the done port, which means that this can automatically generate group_done operation, if you use calyx.primitive's instances, it may not be possible to get the location of the done port in the results(Because there is no guarantee that the last port of the output type in a user-defined component is a done port,although the done port doesn't appear in the code I gave you), and the group_done operation cannot be generated automatically.My idea is that invoke can specify the done port or not, if you use libOps or calyx.component defined components can specify the done port, calyx.primitive instantiated components must specify the done port.Optionally, the user specifying the done port causes the operand of calyx.group_done to be the user-specified port. If the user does not specify, the done port of the invoke instance is used by default.

@linuxlonelyeagle
Copy link
Member

@rachitnigam Hi, I read your implementation of invokde (I didn't read it because I didn't learn rust before) and I found the shortcomings of my implementation and I think I have the answer to my previous question. Regarding the calyx.primitive I mentioned earlier, I think I have found a solution.

@linuxlonelyeagle
Copy link
Member

@rachitnigam Sorry to look like I'm bothering you again.No one here seems to review my pr. Can you help me? Thanks! 😄

@rachitnigam
Copy link
Contributor

I'm currently traveling so will take a look when I'm back

@rachitnigam
Copy link
Contributor

Closed by #5558

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Calyx The Calyx dialect good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants