calyx_opt/analysis/
graph.rs1use calyx_ir::{self as ir, Id, PortIterator, RRC};
2use petgraph::{
3 Direction::{Incoming, Outgoing},
4 algo,
5 graph::{DiGraph, NodeIndex},
6 visit::EdgeRef,
7};
8use std::fmt::{Display, Write};
9use std::{collections::HashMap, rc::Rc};
10
11type Node = RRC<ir::Port>;
12type Edge = ();
13
14pub type CellGraph = DiGraph<Node, Edge>;
17
18#[derive(Clone, Default, Debug)]
36pub struct GraphAnalysis {
37 nodes: HashMap<ir::Canonical, NodeIndex>,
38 graph: CellGraph,
39}
40
41impl From<&ir::Group> for GraphAnalysis {
42 fn from(group: &ir::Group) -> Self {
43 let mut analysis = GraphAnalysis::default();
44
45 for asgn in &group.assignments {
46 analysis.insert_assignment(asgn);
47 }
48
49 analysis
50 }
51}
52
53impl From<&ir::Component> for GraphAnalysis {
54 fn from(component: &ir::Component) -> Self {
55 let mut analysis = GraphAnalysis::default();
56 component.iter_assignments(|asgn| {
57 analysis.insert_assignment(asgn);
58 });
59 component.iter_static_assignments(|asgn| {
60 analysis.insert_assignment(asgn);
61 });
62 analysis
63 }
64}
65
66impl GraphAnalysis {
67 fn insert_assignment<T>(&mut self, asgn: &ir::Assignment<T>) {
68 let GraphAnalysis { nodes, graph } = self;
69 let src_key = asgn.src.borrow().canonical();
71 let dst_key = asgn.dst.borrow().canonical();
72 let src_node = *nodes
73 .entry(src_key)
74 .or_insert_with(|| graph.add_node(Rc::clone(&asgn.src)));
75 let dst_node = *nodes
76 .entry(dst_key)
77 .or_insert_with(|| graph.add_node(Rc::clone(&asgn.dst)));
78 graph.add_edge(src_node, dst_node, ());
79 for port in &asgn.guard.all_ports() {
82 let guard_key = port.borrow().canonical();
83 let idx = *nodes
84 .entry(guard_key)
85 .or_insert_with(|| graph.add_node(Rc::clone(port)));
86 graph.add_edge(idx, dst_node, ());
87 }
88 }
89
90 pub fn reads_from(&self, port: &ir::Port) -> PortIterator<'_> {
93 if let Some(&idx) = self.nodes.get(&port.canonical()) {
94 match port.direction {
95 ir::Direction::Input => PortIterator::empty(),
96 ir::Direction::Output | ir::Direction::Inout => {
97 PortIterator::new(Box::new(
98 self.graph.edges_directed(idx, Outgoing).map(
99 move |edge| {
100 let node_idx = self
101 .graph
102 .edge_endpoints(edge.id())
103 .unwrap()
104 .1;
105 Rc::clone(&self.graph[node_idx])
106 },
107 ),
108 ))
109 }
110 }
111 } else {
112 PortIterator::empty()
113 }
114 }
115
116 pub fn writes_to(&self, port: &ir::Port) -> PortIterator<'_> {
119 if let Some(&idx) = self.nodes.get(&port.canonical()) {
120 match port.direction {
121 ir::Direction::Input | ir::Direction::Inout => {
122 return PortIterator::new(Box::new(
123 self.graph.edges_directed(idx, Incoming).map(
124 move |edge| {
125 let node_idx = self
126 .graph
127 .edge_endpoints(edge.id())
128 .unwrap()
129 .0;
130 Rc::clone(&self.graph[node_idx])
131 },
132 ),
133 ));
134 }
135 ir::Direction::Output => (),
136 }
137 }
138 PortIterator::empty()
139 }
140
141 pub fn add_edges(self, edges: &[(RRC<ir::Port>, RRC<ir::Port>)]) -> Self {
143 let Self { graph, nodes } = self;
144 let mut graph = graph;
145 for (a_ref, b_ref) in edges {
146 let a = a_ref.borrow();
147 let b = b_ref.borrow();
148 if let (Some(a_idx), Some(b_idx)) =
149 (nodes.get(&a.canonical()), nodes.get(&b.canonical()))
150 {
151 graph.add_edge(*a_idx, *b_idx, ());
152 }
153 }
154
155 Self { nodes, graph }
156 }
157
158 pub fn toposort(&self) -> PortIterator<'_> {
160 PortIterator::new(Box::new(
161 algo::toposort(&self.graph, None)
162 .unwrap()
163 .into_iter()
164 .map(move |node_idx| Rc::clone(&self.graph[node_idx])),
165 ))
166 }
167
168 pub fn paths(
170 &self,
171 start: &ir::Port,
172 finish: &ir::Port,
173 ) -> Vec<Vec<RRC<ir::Port>>> {
174 let start_idx = self.nodes.get(&start.canonical()).unwrap();
175 let finish_idx = self.nodes.get(&finish.canonical()).unwrap();
176
177 let paths: Vec<Vec<RRC<ir::Port>>> = algo::all_simple_paths(
178 &self.graph,
179 *start_idx,
180 *finish_idx,
181 0,
182 None,
183 )
184 .map(|v: Vec<_>| {
185 v.into_iter()
186 .map(|i| Rc::clone(&self.graph[NodeIndex::new(i.index())]))
187 .collect()
188 })
189 .collect();
190 paths
191 }
192
193 pub fn edge_induced_subgraph<F>(self, mut filter: F) -> Self
200 where
201 F: FnMut(&ir::Port, &ir::Port) -> bool,
202 {
203 let Self { graph, nodes } = self;
204 let graph = graph.filter_map(
205 |_, node| Some(Rc::clone(node)),
206 |idx, _| {
207 let (src_idx, dst_idx) = graph.edge_endpoints(idx).unwrap();
208 if filter(&graph[src_idx].borrow(), &graph[dst_idx].borrow()) {
209 Some(())
210 } else {
211 None
212 }
213 },
214 );
215 Self { nodes, graph }
216 }
217
218 pub fn ports(&self) -> Vec<RRC<ir::Port>> {
220 self.graph
221 .raw_nodes()
222 .iter()
223 .map(|node| Rc::clone(&node.weight))
224 .collect()
225 }
226
227 pub fn remove_isolated_vertices(self) -> Self {
229 let mut num_neighbors: HashMap<(Id, Id), usize> = HashMap::new();
232
233 let Self { graph, nodes } = self;
234 for n_idx in graph.node_indices() {
235 let node = graph[n_idx].borrow();
236 num_neighbors.insert(
237 (node.get_parent_name(), node.name),
238 graph.neighbors_undirected(n_idx).count(),
239 );
240 }
241 let mut graph_copy = graph.clone();
242 let mut nodes_copy = nodes;
243
244 graph_copy.retain_nodes(|_g, n_idx| {
245 let node = graph[n_idx].borrow();
246 *num_neighbors
247 .get(&(node.get_parent_name(), node.name))
248 .unwrap()
249 > 0
250 });
251
252 for node in graph_copy.raw_nodes() {
254 let port = node.weight.borrow();
255 let n_idx = graph_copy
256 .node_indices()
257 .find(|idx| *graph_copy[*idx].borrow() == *port)
258 .unwrap();
259 nodes_copy.insert(port.canonical(), n_idx);
260 }
261
262 Self {
263 graph: graph_copy,
264 nodes: nodes_copy,
265 }
266 }
267
268 pub fn has_cycles(&self) -> bool {
270 algo::is_cyclic_directed(&self.graph)
271 }
272}
273
274impl Display for GraphAnalysis {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 let mut out = String::new();
277 for idx in self.graph.node_indices() {
278 let src_port = self.graph[idx].borrow();
279 let src =
280 format!("{}.{}", src_port.get_parent_name(), src_port.name);
281 writeln!(
282 &mut out,
283 "{} -> [{}]",
284 src,
285 self.graph
286 .neighbors_directed(idx, petgraph::Direction::Outgoing)
287 .map(|idx| {
288 let port = self.graph[idx].borrow();
289 format!("{}.{}", port.get_parent_name(), port.name)
290 })
291 .collect::<Vec<String>>()
292 .join(", ")
293 )
294 .expect("Failed to write to ScheduleConflicts string");
295 }
296 out.fmt(f)
297 }
298}