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

Group to Seq #1086

Merged
merged 34 commits into from
Jul 21, 2022
Merged

Group to Seq #1086

merged 34 commits into from
Jul 21, 2022

Conversation

calebmkim
Copy link
Contributor

@calebmkim calebmkim commented Jul 7, 2022

Transforms a big group into a seq of two smaller groups.
OrderAnalysis is used to determine if we can even apply the transformation.

I can writeup more details here, but part of me thinks it may be better to just discuss details in person.

Edit: Failing bc it's applying the transformation and that is changing some of the the things in the examples/ folder,
and changing the number of cycles for some of the correctness tests.
Edit2: Changed these tests to reflect my changes.

@calebmkim
Copy link
Contributor Author

Couple of questions about this pass, @rachitnigam.

  1. Should we even try to detect groups that have chains of length > 2-- for example, when 3 registers are being written to in sequence within a group? The code I have right now tries (incorrectly) to detect length > 2, but it seems the Calyx code will have to be quite complicated for these situations. Should I just try to focus on chains of length 2?

(More code specific questions)
2) When I call [ir::Start] and iterate through comp.groups, it seems I still have to eventually replace some of the groups with a seq in the control tree-- if so, how do I do this and if not, what should I do instead?

  1. It seems I iterate through comp.groups mutably in order to be able to get ownership of the assignments of the group using drain(..) and a mutable reference to comp in order to build the ir::Builder (sorry if this is confusing). Any thoughts on how to address this?

@rachitnigam
Copy link
Contributor

  1. I think chains of more than 2, as you predict, are going to be hard to detect. You'd have to come up with some way to figure out which registers are being used like an FSM and then use that information to detect what the chain looks like. I think focusing on 2-component chaining is the way to go for now.
  2. Got it! In that case, you can iterate over the groups first and come up with a mapping from Group -> Seq representing all the groups that you need to transform. Then, you can visit the enable and replace the group if it is in the mapping.
  3. Ah, this is a pretty standard problem. ir::Builder lets you directly access the underlying component: builder.component

@calebmkim
Copy link
Contributor Author

calebmkim commented Jul 11, 2022

@rachitnigam (sorry to bother again)
I am still running into problems using builder

 let mut builder = ...; 
 for group in builder.component.groups.iter_mut(){
    ... 
   builder.add_group(...); 
}

This is giving me an error:
It says I have two mutable borrows: one for buider.components.groups.iter_mut() and one for builder.add_group(..). And it has to be iter_mut since if it isn't then I will have an immutable and a mutable borrow.

Also, are there any other benefits to doing this in the start rather than enable method: your earlier comment said it saves traversing the control tree, but from 2) we will have to do that anyways even if we do it through start? I'm asking mainly bc doing it in start is giving me the problem above.

@rachitnigam
Copy link
Contributor

The same group may be used from multiple enable statements:

seq {
  one;
  two;
  one;
}

In this case, you'll end up re-running the analysis for each enable(one).

To iterate over groups, you have to "detach" it from the component:

let groups = component.groups.drain(..).collect();
let builder = ir::Builder(component, ..);
for g in groups { ... }
component.groups = groups;

Does that make sense?

#[derive(Default)]
///Primarily used to help determine the order cells are executed within
///the group
pub struct OrderAnalysis {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this implementing the same logic as InferStaticTiming? If so, we should generalize those two

Copy link
Contributor Author

@calebmkim calebmkim Jul 13, 2022

Choose a reason for hiding this comment

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

There is some overlap between, mem_wrt_dep_graph of InferStaticTiming and update_map of OrderAnalysis but there are differences as well and in my opinion it's not worth it to try to generalize. Edit: But maybe generalizing is a good idea. It's just it doesn't seem like it would be on first glance.

second_group.borrow_mut().assignments.append(&mut snd_asmts);

//renaming old_group[done] = ... to new_group[done] = ...
let new_done = rename_group_done(
Copy link
Contributor

Choose a reason for hiding this comment

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

The right way to update a group's done condition is using Group::done_cond_mut instead of manually editing the assignments. The latter is unsafe and will be deprecated

Copy link
Contributor Author

@calebmkim calebmkim Jul 13, 2022

Choose a reason for hiding this comment

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

If I create a group using builder.add_group(), does it automatically come with a done assignment? Because when I try to use done_cond_mut it's giving me an error and saying the group has no done condition.

CollapseControl,
CompileRef, //Must run before 'resource-sharing'.
GroupToSeq,
Copy link
Contributor

Choose a reason for hiding this comment

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

You can run DeadGroupRemoval again if you want in this pipeline

CollapseControl,
CompileRef, //Must run before 'resource-sharing'.
GroupToSeq,
CellShare,
Copy link
Contributor

Choose a reason for hiding this comment

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

Open an issue saying that we should run CellShare as early as possible, ideally before merge-static-par. This cannot be done currently because it doesn't handle ref cells passed into an invoke: https://docs.calyxir.org/lang/memories-by-reference.html#the-easy-way

Copy link
Contributor

@rachitnigam rachitnigam left a comment

Choose a reason for hiding this comment

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

Okay, this is looking done! A couple of minor comments about structuring the interface for SplitAnalysis. Address it and merge at your discretion!

// Add back the groups we drained at the beginning of this method, but
// filter out the empty groups that were split into smaller groups
comp.groups.append(groups.into_iter().filter(
|group: &ir::RRC<ir::Group>| !group.borrow().assignments.is_empty(),
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to add the type of group here

let mut builder = ir::Builder::new(comp, sigs);
for g in groups.iter() {
let mut group = g.borrow_mut();
match SplitAnalysis::get_split(
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like SplitAnalysis returns two groups in the Ok case. What if we make it take the group directly instead of passing the assignments and the name? That way, we also avoid this drain(..).collect() call.

@@ -1,18 +1,19 @@
import "primitives/core.futil";
import "primitives/binary_operators.futil";
component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) {
component main<"static"=5>(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Really cool effect of this pass that the component can have its static latency inferred!

@@ -0,0 +1,92 @@
// -p group2seq
Copy link
Contributor

Choose a reason for hiding this comment

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

Run -p validate before group2seq

@@ -0,0 +1,43 @@
//-p canonicalize -p group2seq
Copy link
Contributor

Choose a reason for hiding this comment

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

Run validate instead of canonicalize

@@ -0,0 +1,43 @@
//-p canonicalize -p group2seq
Copy link
Contributor

Choose a reason for hiding this comment

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

validate instead of canonicalize

@calebmkim calebmkim merged commit db51f54 into master Jul 21, 2022
@calebmkim calebmkim deleted the group_to_seq branch July 21, 2022 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants