calyx_opt/passes/
infer_share.rs

1use crate::analysis::{DominatorMap, ShareSet};
2use crate::traversal::{
3    Action, ConstructVisitor, Named, Order, ParseVal, PassOpt, VisResult,
4    Visitor,
5};
6use calyx_ir as ir;
7use calyx_utils::{CalyxResult, OutputFile};
8
9/// This pass checks if components are (state) shareable. Here is the process it
10/// goes through: if a component uses any ref cells, or non-shareable cells then it
11/// is automatically not shareable. Otherwise, check if each read of a stateful
12/// cell is guaranteed to be dominated by a write to the same cell-- we check this
13/// by building a domination map. If so, component is state shareable.
14pub struct InferShare {
15    print_dmap: Option<OutputFile>,
16    print_static_analysis: Option<OutputFile>,
17    state_shareable: ShareSet,
18    shareable: ShareSet,
19    //name of main (so we can skip it)
20    main: ir::Id,
21}
22
23impl Named for InferShare {
24    fn name() -> &'static str {
25        "infer-share"
26    }
27
28    fn description() -> &'static str {
29        "Infer User Defined Components as Shareable"
30    }
31
32    fn opts() -> Vec<PassOpt> {
33        vec![
34            PassOpt::new(
35                "print-dmap",
36                "Print the domination map",
37                ParseVal::OutStream(OutputFile::Null),
38                PassOpt::parse_outstream,
39            ),
40            PassOpt::new(
41                "print-static-analysis",
42                "Prints the domination analysis for static dmaps",
43                ParseVal::OutStream(OutputFile::Null),
44                PassOpt::parse_outstream,
45            ),
46        ]
47    }
48}
49
50impl ConstructVisitor for InferShare {
51    fn from(ctx: &ir::Context) -> CalyxResult<Self>
52    where
53        Self: Sized + Named,
54    {
55        let opts = Self::get_opts(ctx);
56
57        let state_shareable = ShareSet::from_context::<true>(ctx);
58        let shareable = ShareSet::from_context::<false>(ctx);
59
60        Ok(InferShare {
61            print_dmap: opts[&"print-dmap"].not_null_outstream(),
62            print_static_analysis: opts[&"print-static-analysis"]
63                .not_null_outstream(),
64            state_shareable,
65            shareable,
66            main: ctx.entrypoint,
67        })
68    }
69
70    fn clear_data(&mut self) {}
71}
72
73impl Visitor for InferShare {
74    fn iteration_order() -> Order {
75        Order::Post
76    }
77    fn start(
78        &mut self,
79        comp: &mut ir::Component,
80        _sigs: &ir::LibrarySignatures,
81        _comps: &[ir::Component],
82    ) -> VisResult {
83        //if the component is main, then we can stop checking
84        if comp.name == self.main {
85            return Ok(Action::Stop);
86        }
87
88        // closure to determine if cell is type ThisComponent or Constant
89        let const_or_this = |cell: &ir::RRC<ir::Cell>| -> bool {
90            matches!(
91                cell.borrow().prototype,
92                ir::CellType::ThisComponent | ir::CellType::Constant { .. }
93            )
94        };
95
96        // returns true if cell is shareble, state_shareable, Constant, or This component
97        let type_is_shareable = |cell: &ir::RRC<ir::Cell>| -> bool {
98            const_or_this(cell)
99                || self.shareable.is_shareable_component(cell)
100                || self.state_shareable.is_shareable_component(cell)
101        };
102
103        // cannot contain any external cells, or any cells of a "non-shareable" type
104        // (i.e. not shareable, state_shareable, const or This component)
105        if comp.cells.iter().any(|cell| {
106            !type_is_shareable(cell) && !cell.borrow().is_reference()
107        }) {
108            return Ok(Action::Stop);
109        }
110
111        // build the domination map
112        let mut dmap =
113            DominatorMap::new(&mut comp.control.borrow_mut(), comp.name);
114
115        // print the domination map if command line argument says so
116        if let Some(s) = &mut self.print_dmap {
117            write!(s.get_write(), "{dmap:?}").unwrap();
118        }
119        if let Some(s) = &mut self.print_static_analysis {
120            write!(s.get_write(), "{:?}", dmap.static_par_domination).unwrap();
121        }
122
123        for (node, dominators) in dmap.map.iter_mut() {
124            //get the reads
125            let reads =
126                DominatorMap::get_node_reads(node, comp, &self.state_shareable);
127
128            //if read and write occur in same group/invoke, then we cannot label it
129            //shareable. So we remove node from its dominators
130            dominators.remove(node);
131            for cell_name in reads {
132                if !DominatorMap::key_written_guaranteed(
133                    cell_name, dominators, comp,
134                ) {
135                    return Ok(Action::Stop);
136                }
137            }
138        }
139        comp.attributes.insert(ir::BoolAttr::StateShare, 1);
140        self.state_shareable.add(comp.name);
141        Ok(Action::Stop)
142    }
143}