1use crate::graph::core::{ComputationGraph, NodeId};
4use crate::graph::operations::Operation;
5use crate::JitResult;
6use std::collections::{HashMap, HashSet, VecDeque};
7
8#[derive(Debug, Clone)]
10pub struct ControlFlowAnalysis {
11 pub dominators: HashMap<NodeId, Option<NodeId>>,
13
14 pub dominated: HashMap<NodeId, HashSet<NodeId>>,
16
17 pub loops: Vec<LoopInfo>,
19
20 pub conditionals: Vec<ConditionalInfo>,
22
23 pub stats: ControlFlowStats,
25}
26
27impl ControlFlowAnalysis {
28 pub fn new() -> Self {
30 Self {
31 dominators: HashMap::new(),
32 dominated: HashMap::new(),
33 loops: Vec::new(),
34 conditionals: Vec::new(),
35 stats: ControlFlowStats::default(),
36 }
37 }
38
39 pub fn analyze(graph: &ComputationGraph) -> JitResult<Self> {
41 let mut analysis = Self::new();
42
43 analysis.compute_dominators(graph)?;
45
46 analysis.detect_loops(graph)?;
48
49 analysis.detect_conditionals(graph)?;
51
52 analysis.compute_statistics(graph);
54
55 Ok(analysis)
56 }
57
58 fn compute_dominators(&mut self, graph: &ComputationGraph) -> JitResult<()> {
60 let nodes: Vec<NodeId> = graph.nodes().map(|(id, _)| id).collect();
62
63 for &node in &nodes {
65 self.dominators.insert(node, None);
66 self.dominated.insert(node, HashSet::new());
67 }
68
69 for &node in &nodes {
71 let mut dominates = HashSet::new();
72
73 for &other_node in &nodes {
76 if node != other_node && self.dominates_node(graph, node, other_node) {
77 dominates.insert(other_node);
78
79 if self
81 .dominators
82 .get(&other_node)
83 .expect("dominator entry should exist")
84 .is_none()
85 {
86 self.dominators.insert(other_node, Some(node));
87 }
88 }
89 }
90
91 self.dominated.insert(node, dominates);
92 }
93
94 Ok(())
95 }
96
97 fn dominates_node(&self, graph: &ComputationGraph, dominator: NodeId, node: NodeId) -> bool {
99 if dominator == node {
103 return true;
104 }
105
106 let inputs = &graph.inputs;
108 if inputs.is_empty() {
109 return false;
110 }
111
112 for &input in inputs {
113 if !self.path_contains_node(graph, input, node, dominator) {
114 return false;
115 }
116 }
117
118 true
119 }
120
121 fn path_contains_node(
123 &self,
124 graph: &ComputationGraph,
125 start: NodeId,
126 end: NodeId,
127 check_node: NodeId,
128 ) -> bool {
129 if start == end {
130 return start == check_node;
131 }
132
133 let mut visited = HashSet::new();
134 let mut queue = VecDeque::new();
135 queue.push_back(start);
136
137 while let Some(current) = queue.pop_front() {
138 if visited.contains(¤t) {
139 continue;
140 }
141 visited.insert(current);
142
143 if current == end {
144 return visited.contains(&check_node);
145 }
146
147 for neighbor in graph.get_node_outputs(current) {
148 if !visited.contains(&neighbor) {
149 queue.push_back(neighbor);
150 }
151 }
152 }
153
154 false
155 }
156
157 fn detect_loops(&mut self, graph: &ComputationGraph) -> JitResult<()> {
159 let nodes: Vec<NodeId> = graph.nodes().map(|(id, _)| id).collect();
160
161 for &node in &nodes {
162 if let Some(node_data) = graph.get_node(node) {
163 match &node_data.operation {
164 Operation::While(while_info) => {
165 let loop_info = LoopInfo {
166 header: node,
167 condition: while_info.condition,
168 body_nodes: self.find_loop_body_nodes(graph, while_info.body),
169 loop_type: LoopType::While,
170 max_iterations: while_info.max_iterations,
171 };
172 self.loops.push(loop_info);
173 }
174 Operation::For(for_info) => {
175 let loop_info = LoopInfo {
176 header: node,
177 condition: for_info.start, body_nodes: self.find_loop_body_nodes(graph, for_info.body),
179 loop_type: LoopType::For,
180 max_iterations: None, };
182 self.loops.push(loop_info);
183 }
184 _ => {}
185 }
186 }
187 }
188
189 Ok(())
190 }
191
192 fn find_loop_body_nodes(
194 &self,
195 graph: &ComputationGraph,
196 body_start: NodeId,
197 ) -> HashSet<NodeId> {
198 let mut body_nodes = HashSet::new();
199 let mut queue = VecDeque::new();
200 queue.push_back(body_start);
201
202 while let Some(node) = queue.pop_front() {
203 if body_nodes.contains(&node) {
204 continue;
205 }
206 body_nodes.insert(node);
207
208 for successor in graph.get_node_outputs(node) {
210 if let Some(successor_data) = graph.get_node(successor) {
211 match &successor_data.operation {
212 Operation::Break | Operation::Continue => {
213 body_nodes.insert(successor);
215 }
216 _ => {
217 if !body_nodes.contains(&successor) {
218 queue.push_back(successor);
219 }
220 }
221 }
222 }
223 }
224 }
225
226 body_nodes
227 }
228
229 fn detect_conditionals(&mut self, graph: &ComputationGraph) -> JitResult<()> {
231 let nodes: Vec<NodeId> = graph.nodes().map(|(id, _)| id).collect();
232
233 for &node in &nodes {
234 if let Some(node_data) = graph.get_node(node) {
235 if let Operation::If(if_info) = &node_data.operation {
236 let then_nodes = self.find_branch_nodes(graph, if_info.then_block);
237 let else_nodes = if let Some(else_block) = if_info.else_block {
238 self.find_branch_nodes(graph, else_block)
239 } else {
240 HashSet::new()
241 };
242
243 let conditional_info = ConditionalInfo {
244 condition_node: if_info.condition,
245 then_nodes,
246 else_nodes,
247 merge_point: if_info.merge_point,
248 };
249 self.conditionals.push(conditional_info);
250 }
251 }
252 }
253
254 Ok(())
255 }
256
257 fn find_branch_nodes(&self, graph: &ComputationGraph, branch_start: NodeId) -> HashSet<NodeId> {
259 let mut branch_nodes = HashSet::new();
260 let mut queue = VecDeque::new();
261 queue.push_back(branch_start);
262
263 while let Some(node) = queue.pop_front() {
264 if branch_nodes.contains(&node) {
265 continue;
266 }
267 branch_nodes.insert(node);
268
269 for successor in graph.get_node_outputs(node) {
271 if let Some(successor_data) = graph.get_node(successor) {
272 match &successor_data.operation {
273 Operation::Merge(_) => {
274 break;
276 }
277 _ => {
278 if !branch_nodes.contains(&successor) {
279 queue.push_back(successor);
280 }
281 }
282 }
283 }
284 }
285 }
286
287 branch_nodes
288 }
289
290 fn compute_statistics(&mut self, graph: &ComputationGraph) {
292 let mut loop_count = 0;
293 let mut conditional_count = 0;
294 let mut block_count = 0;
295
296 for (_, node) in graph.nodes() {
297 match &node.operation {
298 Operation::While(_) | Operation::For(_) => loop_count += 1,
299 Operation::If(_) => conditional_count += 1,
300 Operation::Block(_) => block_count += 1,
301 _ => {}
302 }
303 }
304
305 self.stats = ControlFlowStats {
306 total_nodes: graph.node_count(),
307 loop_count,
308 conditional_count,
309 block_count,
310 max_loop_depth: self.compute_max_loop_depth(),
311 max_conditional_depth: self.compute_max_conditional_depth(),
312 };
313 }
314
315 fn compute_max_loop_depth(&self) -> usize {
317 if self.loops.is_empty() {
319 0
320 } else {
321 1 }
323 }
324
325 fn compute_max_conditional_depth(&self) -> usize {
327 if self.conditionals.is_empty() {
329 0
330 } else {
331 1 }
333 }
334
335 pub fn is_in_loop(&self, node: NodeId) -> bool {
337 self.loops
338 .iter()
339 .any(|loop_info| loop_info.body_nodes.contains(&node))
340 }
341
342 pub fn is_in_conditional(&self, node: NodeId) -> bool {
344 self.conditionals.iter().any(|cond_info| {
345 cond_info.then_nodes.contains(&node) || cond_info.else_nodes.contains(&node)
346 })
347 }
348
349 pub fn containing_loop(&self, node: NodeId) -> Option<&LoopInfo> {
351 self.loops
352 .iter()
353 .find(|loop_info| loop_info.body_nodes.contains(&node))
354 }
355
356 pub fn containing_conditional(&self, node: NodeId) -> Option<&ConditionalInfo> {
358 self.conditionals.iter().find(|cond_info| {
359 cond_info.then_nodes.contains(&node) || cond_info.else_nodes.contains(&node)
360 })
361 }
362}
363
364impl Default for ControlFlowAnalysis {
365 fn default() -> Self {
366 Self::new()
367 }
368}
369
370#[derive(Debug, Clone)]
372pub struct LoopInfo {
373 pub header: NodeId,
375 pub condition: NodeId,
377 pub body_nodes: HashSet<NodeId>,
379 pub loop_type: LoopType,
381 pub max_iterations: Option<usize>,
383}
384
385#[derive(Debug, Clone, PartialEq, Eq)]
387pub enum LoopType {
388 While,
389 For,
390 DoWhile,
391}
392
393#[derive(Debug, Clone)]
395pub struct ConditionalInfo {
396 pub condition_node: NodeId,
398 pub then_nodes: HashSet<NodeId>,
400 pub else_nodes: HashSet<NodeId>,
402 pub merge_point: Option<NodeId>,
404}
405
406#[derive(Debug, Clone, Default)]
408pub struct ControlFlowStats {
409 pub total_nodes: usize,
411 pub loop_count: usize,
413 pub conditional_count: usize,
415 pub block_count: usize,
417 pub max_loop_depth: usize,
419 pub max_conditional_depth: usize,
421}