pub struct TopDownCompileControl { /* private fields */ }
Expand description

Core lowering pass. Compiles away the control programs in components into purely structural code using an finite-state machine (FSM).

Lowering operates in two steps:

  1. Compile all ir::Par control sub-programs into a single ir::Enable of a group that runs all children to completion.
  2. Compile the top-level control program into a single ir::Enable.

Compiling non-par programs

At very high-level, the pass assigns an FSM state to each ir::Enable in the program and generates transitions to the state to activate the groups contained within the ir::Enable.

The compilation process calculates all predeccesors of the ir::Enable while walking over the control program. A predeccesor is any enable statement that can directly “jump” to the current ir::Enable. The compilation process computes all such predeccesors and the guards that need to be true for the predeccesor to jump into this enable statement.

cond0;
while lt.out {
  if gt.out { true } else { false }
}
next;

The predeccesor sets are:

cond0 -> []
true -> [(cond0, lt.out & gt.out); (true; lt.out & gt.out); (false, lt.out & !gt.out)]
false -> [(cond0, lt.out & !gt.out); (true; lt.out & gt.out); (false, lt.out & !gt.out)]
next -> [(cond0, !lt.out); (true, !lt.out); (false, !lt.out)]

Compiling ir::Enable

The process first takes all edges from predeccesors and transitions to the state for this enable and enables the group in this state:

let cur_state; // state of this enable
for (state, guard) in predeccesors:
  transitions.insert(state, cur_state, guard)
enables.insert(cur_state, group)

While this process will generate a functioning FSM, the FSM takes unnecessary cycles for FSM transitions.

For example:

seq { one; two; }

The FSM generated will look like this (where f is the FSM register):

f.in = one[done] ? 1;
f.in = two[done] ? 2;
one[go] = !one[done] & f.out == 0;
two[go] = !two[done] & f.out == 1;

The cycle-level timing for this FSM will look like: - cycle 0: (f.out == 0), enable one - cycle t: (f.out == 0), (one[done] == 1), disable one - cycle t+1: (f.out == 1), enable two - cycle t+l: (f.out == 1), (two[done] == 1), disable two - cycle t+l+1: finish

The transition t -> t+1 represents one where group one is done but group two hasn’t started executing.

To address this specific problem, there is an additional enable added to run all groups within an enable while the FSM is transitioning. The final transition will look like this:

f.in = one[done] ? 1;
f.in = two[done] ? 2;
one[go] = !one[done] & f.out == 0;
two[go] = (!two[done] & f.out == 1) || (one[done] & f.out == 0);

Note that !two[done] isn’t present in the second disjunct because all groups are guaranteed to run for at least one cycle and the second disjunct will only be true for one cycle before the first disjunct becomes true.

Compiling par programs

We have to generate new FSM-based controller for each child of a par node so that each child can indepdendently make progress. If we tie the children to one top-level FSM, their transitions would become interdependent and reduce available concurrency.

Compilation guarantee

At the end of this pass, the control program will have no more than one group enable in it.

Trait Implementations

Construct the visitor using information from the Context
Clear the data stored in the visitor. Called before traversing the next component by [ir::traversal::Visitor]. Read more
The name of a pass. Is used for identifying passes.
A short description of the pass.
Set of options that can be passed to the pass. The options contains a tuple of the option name and a description. Read more

Compile each child in par block separately so each child can make progress indepdendently.

Executed before the traversal begins.
Executed after visiting the children of a ir::Seq node.
Executed after visiting the children of a ir::If node.
Executed after visiting the children of a ir::While node.
Executed after the traversal ends. This method is always invoked regardless of the Action returned from the children. Read more
Precondition for this pass to run on the program. If this function returns None, the pass triggers. Otherwise it aborts and logs the string as the reason. Read more
Transform the ir::Context before visiting the components.
Transform the ir::Context after visiting the components.
Define the iteration order in which components should be visited
Define the traversal over a component. Calls Visitor::start, visits each control node, and finally calls Visitor::finish. Read more
Run the visitor on a given program ir::Context. The function mutably borrows the control program in each component and traverses it. Read more
Build a Default implementation of this pass and call Visitor::do_pass using it. Read more
Executed before visiting the children of a ir::Seq node.
Executed before visiting the children of a ir::Par node.
Executed before visiting the children of a ir::If node.
Executed before visiting the children of a ir::While node.
Executed before visiting the children of a ir::Repeat node.
Executed after visiting the children of a ir::Repeat node.
Executed before visiting the contents of an ir::StaticControl node.
Executed after visiting the conetnts of an ir::StaticControl node.
Executed at an ir::Enable node.
Executed at an ir::StaticEnable node.
Executed before visiting the children of a ir::StaticIf node.
Executed after visiting the children of a ir::StaticIf node.
Executed before visiting the children of a ir::StaticRepeat node.
Executed after visiting the children of a ir::StaticRepeat node.
Executed at an ir::Invoke node.
Executed at a ir::StaticInvoke node.
Executed at an ir::Empty node.

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.