calyx_opt/passes_experimental/sync/
compile_sync.rsuse crate::traversal::{Action, Named, VisResult, Visitor};
use calyx_ir::RRC;
use calyx_ir::{self as ir, GetAttributes};
use calyx_ir::{build_assignments, guard, structure};
use calyx_utils::{CalyxResult, Error};
use linked_hash_map::LinkedHashMap;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
#[derive(Default)]
pub struct CompileSync {
barriers: BarrierMap,
}
type BarrierMap = LinkedHashMap<u64, ([RRC<ir::Cell>; 2], [RRC<ir::Group>; 3])>;
impl Named for CompileSync {
fn name() -> &'static str {
"compile-sync"
}
fn description() -> &'static str {
"Implement barriers for statements marked with @sync attribute"
}
}
fn count_barriers(
s: &ir::Control,
count: &mut HashSet<u64>,
) -> CalyxResult<()> {
match s {
ir::Control::Empty(_) => {
if let Some(n) = s.get_attributes().get(ir::NumAttr::Sync) {
count.insert(n);
}
Ok(())
}
ir::Control::Seq(seq) => {
for stmt in seq.stmts.iter() {
count_barriers(stmt, count)?;
}
Ok(())
}
ir::Control::While(ir::While { body, .. })
| ir::Control::Repeat(ir::Repeat { body, .. }) => {
count_barriers(body, count)?;
Ok(())
}
ir::Control::If(i) => {
count_barriers(&i.tbranch, count)?;
count_barriers(&i.fbranch, count)?;
Ok(())
}
ir::Control::Enable(e) => {
if s.get_attributes().get(ir::NumAttr::Sync).is_some() {
return Err(Error::malformed_control(
"Enable or Invoke controls cannot be marked with @sync"
.to_string(),
)
.with_pos(&e.attributes));
}
Ok(())
}
ir::Control::Invoke(i) => {
if s.get_attributes().get(ir::NumAttr::Sync).is_some() {
return Err(Error::malformed_control(
"Enable or Invoke controls cannot be marked with @sync"
.to_string(),
)
.with_pos(&i.attributes));
}
Ok(())
}
ir::Control::Par(_) => Ok(()),
ir::Control::Static(_) => Ok(()),
}
}
impl CompileSync {
fn build_barriers(
&mut self,
builder: &mut ir::Builder,
s: &mut ir::Control,
count: &mut HashMap<u64, u64>,
) {
match s {
ir::Control::Empty(_) => {
if let Some(ref n) = s.get_attributes().get(ir::NumAttr::Sync) {
if self.barriers.get(n).is_none() {
self.add_shared_structure(builder, n);
}
let (cells, groups) = &self.barriers[n];
let member_idx = count[n];
let mut new_s =
build_member(builder, cells, groups, &member_idx);
std::mem::swap(s, &mut new_s);
}
}
ir::Control::Seq(seq) => {
for stmt in seq.stmts.iter_mut() {
self.build_barriers(builder, stmt, count);
}
}
ir::Control::While(w) => {
self.build_barriers(builder, &mut w.body, count);
}
ir::Control::If(i) => {
self.build_barriers(builder, &mut i.tbranch, count);
self.build_barriers(builder, &mut i.fbranch, count);
}
_ => {}
}
}
fn add_shared_structure(
&mut self,
builder: &mut ir::Builder,
barrier_idx: &u64,
) {
structure!(builder;
let barrier = prim std_sync_reg(32);
let eq = prim std_eq(32);
);
let restore = build_restore(builder, &barrier);
let wait_restore = build_wait_restore(builder, &eq);
let clear_barrier = build_clear_barrier(builder, &barrier);
let shared_cells: [RRC<ir::Cell>; 2] = [barrier, eq];
let shared_groups: [RRC<ir::Group>; 3] =
[wait_restore, restore, clear_barrier];
let info = (shared_cells, shared_groups);
self.barriers.insert(*barrier_idx, info);
}
}
fn build_incr_barrier(
builder: &mut ir::Builder,
barrier: &RRC<ir::Cell>,
save: &RRC<ir::Cell>,
member_idx: &u64,
) -> RRC<ir::Group> {
let group = builder.add_group("incr_barrier");
structure!(builder;
let incr = prim std_add(32);
let cst_1 = constant(1, 1);
let cst_2 = constant(1, 32););
let read_done_guard = guard!(barrier[format!("read_done_{member_idx}")]);
let assigns = build_assignments!(builder;
barrier[format!("read_en_{member_idx}")] = ?cst_1["out"];
incr["left"] = ? barrier[format!("out_{member_idx}")];
incr["right"] = ? cst_2["out"];
save["in"] = read_done_guard? incr["out"];
save["write_en"] = ? barrier[format!("read_done_{member_idx}")];
group["done"] = ?save["done"];
);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_write_barrier(
builder: &mut ir::Builder,
barrier: &RRC<ir::Cell>,
save: &RRC<ir::Cell>,
member_idx: &u64,
) -> RRC<ir::Group> {
let group = builder.add_group("write_barrier");
structure!(builder;
let cst_1 = constant(1, 1););
let assigns = build_assignments!(builder;
barrier[format!("write_en_{member_idx}")] = ?cst_1["out"];
barrier[format!("in_{member_idx}")] = ?save["out"];
group["done"] = ?barrier[format!("write_done_{member_idx}")];
);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_wait(builder: &mut ir::Builder, eq: &RRC<ir::Cell>) -> RRC<ir::Group> {
let group = builder.add_group("wt");
structure!(builder;
let wait_reg = prim std_reg(1);
let cst_1 = constant(1, 1););
let eq_guard = guard!(eq["out"]);
let assigns = build_assignments!(builder;
wait_reg["in"] = ?eq["out"];
wait_reg["write_en"] = eq_guard? cst_1["out"];
group["done"] = ?wait_reg["done"];);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_clear_barrier(
builder: &mut ir::Builder,
barrier: &RRC<ir::Cell>,
) -> RRC<ir::Group> {
let group = builder.add_group("clear_barrier");
structure!(builder;
let cst_1 = constant(1, 1););
let assigns = build_assignments!(builder;
barrier["read_en_0"] = ?cst_1["out"];
group["done"] = ?barrier["read_done_0"];
);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_restore(
builder: &mut ir::Builder,
barrier: &RRC<ir::Cell>,
) -> RRC<ir::Group> {
let group = builder.add_group("restore");
structure!(builder;
let cst_1 = constant(1,1);
let cst_2 = constant(0, 32););
let assigns = build_assignments!(builder;
barrier["write_en_0"] = ?cst_1["out"];
barrier["in_0"] = ?cst_2["out"];
group["done"] = ?barrier["write_done_0"];
);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_wait_restore(
builder: &mut ir::Builder,
eq: &RRC<ir::Cell>,
) -> RRC<ir::Group> {
let group = builder.add_group("wait_restore");
structure!(builder;
let wait_restore_reg = prim std_reg(1);
let cst_1 = constant(1, 1););
let eq_guard = !guard!(eq["out"]);
let assigns = build_assignments!(builder;
wait_restore_reg["in"] = eq_guard? cst_1["out"];
wait_restore_reg["write_en"] = eq_guard? cst_1["out"];
group["done"] = ?wait_restore_reg["done"];
);
group.borrow_mut().assignments.extend(assigns);
group
}
fn build_member(
builder: &mut ir::Builder,
cells: &[RRC<ir::Cell>; 2],
groups: &[RRC<ir::Group>; 3],
member_idx: &u64,
) -> ir::Control {
let mut stmts: Vec<ir::Control> = Vec::new();
let barrier = Rc::clone(&cells[0]);
let eq = Rc::clone(&cells[1]);
let wait_restore = Rc::clone(&groups[0]);
let restore = Rc::clone(&groups[1]);
let clear_barrier = Rc::clone(&groups[2]);
structure!(builder;
let save = prim std_reg(32););
let incr_barrier =
build_incr_barrier(builder, &barrier, &save, &(member_idx - 1));
let write_barrier =
build_write_barrier(builder, &barrier, &save, &(member_idx - 1));
let wait = build_wait(builder, &eq);
stmts.push(ir::Control::enable(incr_barrier));
stmts.push(ir::Control::enable(write_barrier));
stmts.push(ir::Control::enable(wait));
if member_idx == &1 {
stmts.push(ir::Control::enable(clear_barrier));
stmts.push(ir::Control::enable(restore));
} else {
stmts.push(ir::Control::enable(wait_restore));
}
ir::Control::seq(stmts)
}
impl Visitor for CompileSync {
fn finish_par(
&mut self,
s: &mut ir::Par,
comp: &mut ir::Component,
sigs: &ir::LibrarySignatures,
_comps: &[ir::Component],
) -> VisResult {
let mut builder = ir::Builder::new(comp, sigs);
let mut barrier_count: HashMap<u64, u64> = HashMap::new();
for stmt in s.stmts.iter_mut() {
let mut cnt: HashSet<u64> = HashSet::new();
count_barriers(stmt, &mut cnt)?;
for barrier in cnt {
barrier_count
.entry(barrier)
.and_modify(|count| *count += 1)
.or_insert(1);
}
self.build_barriers(&mut builder, stmt, &mut barrier_count);
}
if self.barriers.is_empty() {
return Ok(Action::Continue);
}
let mut init_barriers: Vec<ir::Control> = Vec::new();
for (n, (cells, groups)) in self.barriers.iter() {
let barrier = Rc::clone(&cells[0]);
let eq = Rc::clone(&cells[1]);
let restore = Rc::clone(&groups[1]);
let n_members = barrier_count.get(n).unwrap();
structure!(builder;
let num_members = constant(*n_members, 32);
);
let assigns = build_assignments!(builder;
eq["left"] = ?barrier["peek"];
eq["right"] = ?num_members["out"];
);
builder.component.continuous_assignments.extend(assigns);
init_barriers.push(ir::Control::enable(restore));
}
let mut changed_sequence: Vec<ir::Control> =
vec![ir::Control::par(init_barriers)];
let mut copied_par_stmts: Vec<ir::Control> = Vec::new();
for con in s.stmts.drain(..) {
copied_par_stmts.push(con);
}
changed_sequence.push(ir::Control::par(copied_par_stmts));
Ok(Action::change(ir::Control::seq(changed_sequence)))
}
}