1use std::sync::Arc;
6use std::collections::{HashMap, HashSet, VecDeque};
7use crate::storage::UnifiedGraphStore;
8use crate::error::Result;
9use crate::types::{SymbolId, BlockId, PathId, PathKind};
10
11#[derive(Clone)]
30pub struct CfgModule {
31 store: Arc<UnifiedGraphStore>,
32}
33
34impl CfgModule {
35 pub(crate) fn new(store: Arc<UnifiedGraphStore>) -> Self {
36 Self { store }
37 }
38
39 pub async fn index(&self) -> Result<()> {
48 Ok(())
51 }
52
53 pub fn paths(&self, function: SymbolId) -> PathBuilder {
59 PathBuilder {
60 module: self.clone(),
61 function_id: function,
62 normal_only: false,
63 error_only: false,
64 max_length: None,
65 limit: None,
66 }
67 }
68
69 pub async fn dominators(&self, function: SymbolId) -> Result<DominatorTree> {
77 let _ = function;
80 let mut dominators = HashMap::new();
81 dominators.insert(BlockId(0), BlockId(0));
82 Ok(DominatorTree {
83 root: BlockId(0),
84 dominators,
85 })
86 }
87
88 pub async fn loops(&self, function: SymbolId) -> Result<Vec<Loop>> {
96 let _ = function;
99 Ok(Vec::new())
100 }
101}
102
103#[derive(Clone)]
109pub struct PathBuilder {
110 module: CfgModule,
111 function_id: SymbolId,
112 normal_only: bool,
113 error_only: bool,
114 max_length: Option<usize>,
115 limit: Option<usize>,
116}
117
118impl PathBuilder {
119 pub fn normal_only(mut self) -> Self {
121 self.normal_only = true;
122 self.error_only = false;
123 self
124 }
125
126 pub fn error_only(mut self) -> Self {
128 self.normal_only = false;
129 self.error_only = true;
130 self
131 }
132
133 pub fn max_length(mut self, n: usize) -> Self {
139 self.max_length = Some(n);
140 self
141 }
142
143 pub fn limit(mut self, n: usize) -> Self {
149 self.limit = Some(n);
150 self
151 }
152
153 pub async fn execute(self) -> Result<Vec<Path>> {
162 let path = Path::new(vec![BlockId(0)]);
165 Ok(vec![path])
166 }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
171pub struct DominatorTree {
172 pub root: BlockId,
174 pub dominators: HashMap<BlockId, BlockId>,
176}
177
178impl DominatorTree {
179 pub fn new(root: BlockId) -> Self {
181 Self {
182 root,
183 dominators: HashMap::new(),
184 }
185 }
186
187 pub fn immediate_dominator(&self, block: BlockId) -> Option<BlockId> {
189 self.dominators.get(&block).copied()
190 }
191
192 pub fn dominates(&self, dominator: BlockId, block: BlockId) -> bool {
194 if dominator == block {
195 return true;
196 }
197 if dominator == self.root {
198 return true;
199 }
200 let mut current = block;
201 while let Some(idom) = self.dominators.get(¤t) {
202 if *idom == dominator {
203 return true;
204 }
205 current = *idom;
206 }
207 false
208 }
209
210 pub fn insert(&mut self, block: BlockId, dominator: BlockId) {
212 self.dominators.insert(block, dominator);
213 }
214
215 pub fn len(&self) -> usize {
217 self.dominators.len() + 1
218 }
219
220 pub fn is_empty(&self) -> bool {
222 self.dominators.is_empty()
223 }
224}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct Loop {
229 pub header: BlockId,
231 pub blocks: Vec<BlockId>,
233 pub depth: usize,
235}
236
237impl Loop {
238 pub fn new(header: BlockId) -> Self {
240 Self {
241 header,
242 blocks: Vec::new(),
243 depth: 0,
244 }
245 }
246
247 pub fn with_blocks(header: BlockId, blocks: Vec<BlockId>) -> Self {
249 Self {
250 header,
251 blocks,
252 depth: 0,
253 }
254 }
255
256 pub fn with_depth(header: BlockId, blocks: Vec<BlockId>, depth: usize) -> Self {
258 Self {
259 header,
260 blocks,
261 depth,
262 }
263 }
264
265 pub fn contains(&self, block: BlockId) -> bool {
267 self.header == block || self.blocks.contains(&block)
268 }
269
270 pub fn len(&self) -> usize {
272 self.blocks.len() + 1
273 }
274
275 pub fn is_empty(&self) -> bool {
277 self.blocks.is_empty()
278 }
279}
280
281#[derive(Clone, Debug, PartialEq, Eq)]
283pub struct Path {
284 pub id: PathId,
286 pub kind: PathKind,
288 pub blocks: Vec<BlockId>,
290 pub length: usize,
292}
293
294impl Path {
295 pub fn new(blocks: Vec<BlockId>) -> Self {
297 let length = blocks.len();
298 let mut hasher = blake3::Hasher::new();
299 for block in &blocks {
300 hasher.update(&block.0.to_le_bytes());
301 }
302 let hash = hasher.finalize();
303 let mut id = [0u8; 16];
304 id.copy_from_slice(&hash.as_bytes()[0..16]);
305
306 Self {
307 id: PathId(id),
308 kind: PathKind::Normal,
309 blocks,
310 length,
311 }
312 }
313
314 pub fn with_kind(blocks: Vec<BlockId>, kind: PathKind) -> Self {
316 let length = blocks.len();
317 let mut hasher = blake3::Hasher::new();
318 for block in &blocks {
319 hasher.update(&block.0.to_le_bytes());
320 }
321 let hash = hasher.finalize();
322 let mut id = [0u8; 16];
323 id.copy_from_slice(&hash.as_bytes()[0..16]);
324
325 Self {
326 id: PathId(id),
327 kind,
328 blocks,
329 length,
330 }
331 }
332
333 pub fn is_normal(&self) -> bool {
335 self.kind == PathKind::Normal
336 }
337
338 pub fn is_error(&self) -> bool {
340 self.kind == PathKind::Error
341 }
342
343 pub fn contains(&self, block: BlockId) -> bool {
345 self.blocks.contains(&block)
346 }
347
348 pub fn entry(&self) -> Option<BlockId> {
350 self.blocks.first().copied()
351 }
352
353 pub fn exit(&self) -> Option<BlockId> {
355 self.blocks.last().copied()
356 }
357}
358
359#[derive(Clone, Debug)]
361pub struct TestCfg {
362 pub entry: BlockId,
364 pub exits: HashSet<BlockId>,
366 pub error_blocks: HashSet<BlockId>,
368 pub successors: HashMap<BlockId, Vec<BlockId>>,
370 pub predecessors: HashMap<BlockId, Vec<BlockId>>,
372}
373
374impl TestCfg {
375 pub fn new(entry: BlockId) -> Self {
377 Self {
378 entry,
379 exits: HashSet::new(),
380 error_blocks: HashSet::new(),
381 successors: HashMap::new(),
382 predecessors: HashMap::new(),
383 }
384 }
385
386 pub fn add_edge(&mut self, from: BlockId, to: BlockId) -> &mut Self {
388 self.successors.entry(from).or_default().push(to);
389 self.predecessors.entry(to).or_default().push(from);
390 self
391 }
392
393 pub fn add_exit(&mut self, block: BlockId) -> &mut Self {
395 self.exits.insert(block);
396 self
397 }
398
399 pub fn add_error(&mut self, block: BlockId) -> &mut Self {
401 self.error_blocks.insert(block);
402 self
403 }
404
405 pub fn chain(start: i64, count: usize) -> Self {
407 let mut cfg = Self::new(BlockId(start));
408 for i in start..(start + count as i64 - 1) {
409 cfg.add_edge(BlockId(i), BlockId(i + 1));
410 }
411 cfg.add_exit(BlockId(start + count as i64 - 1));
412 cfg
413 }
414
415 pub fn if_else() -> Self {
417 let mut cfg = Self::new(BlockId(0));
418 cfg.add_edge(BlockId(0), BlockId(1))
419 .add_edge(BlockId(0), BlockId(2))
420 .add_edge(BlockId(1), BlockId(3))
421 .add_edge(BlockId(2), BlockId(3))
422 .add_exit(BlockId(3));
423 cfg
424 }
425
426 pub fn simple_loop() -> Self {
428 let mut cfg = Self::new(BlockId(0));
429 cfg.add_edge(BlockId(0), BlockId(1))
430 .add_edge(BlockId(1), BlockId(2))
431 .add_edge(BlockId(2), BlockId(1))
432 .add_edge(BlockId(1), BlockId(3))
433 .add_exit(BlockId(3));
434 cfg
435 }
436
437 pub fn enumerate_paths(&self) -> Vec<Path> {
439 let mut paths = Vec::new();
440 let mut current = vec![self.entry];
441 let mut visited = HashSet::new();
442 self.dfs(&mut paths, &mut current, &mut visited, self.entry);
443 paths
444 }
445
446 fn dfs(&self, paths: &mut Vec<Path>, current: &mut Vec<BlockId>, visited: &mut HashSet<BlockId>, block: BlockId) {
447 if self.exits.contains(&block) {
448 paths.push(Path::new(current.clone()));
449 return;
450 }
451 if visited.contains(&block) {
452 return;
453 }
454 visited.insert(block);
455 if let Some(successors) = self.successors.get(&block) {
456 for &succ in successors {
457 current.push(succ);
458 self.dfs(paths, current, visited, succ);
459 current.pop();
460 }
461 }
462 visited.remove(&block);
463 }
464
465 pub fn compute_dominators(&self) -> DominatorTree {
467 let mut blocks: HashSet<BlockId> = HashSet::new();
468 blocks.insert(self.entry);
469 for (from, tos) in &self.successors {
470 blocks.insert(*from);
471 for to in tos {
472 blocks.insert(*to);
473 }
474 }
475
476 if blocks.is_empty() {
477 return DominatorTree::new(self.entry);
478 }
479
480 let block_list: Vec<BlockId> = blocks.iter().copied().collect();
481 let mut dom: HashMap<BlockId, HashSet<BlockId>> = HashMap::new();
482
483 for &block in &block_list {
484 if block == self.entry {
485 dom.insert(block, HashSet::from([self.entry]));
486 } else {
487 dom.insert(block, blocks.clone());
488 }
489 }
490
491 let mut changed = true;
492 while changed {
493 changed = false;
494 for &block in &block_list {
495 if block == self.entry {
496 continue;
497 }
498 let preds = self.predecessors.get(&block);
499 if preds.is_none() || preds.unwrap().is_empty() {
500 continue;
501 }
502 let mut new_dom: HashSet<BlockId> = dom.get(&preds.unwrap()[0]).cloned().unwrap_or_default();
503 for pred in &preds.unwrap()[1..] {
504 if let Some(pred_dom) = dom.get(pred) {
505 new_dom = new_dom.intersection(pred_dom).copied().collect();
506 }
507 }
508 new_dom.insert(block);
509 if dom.get(&block) != Some(&new_dom) {
510 dom.insert(block, new_dom);
511 changed = true;
512 }
513 }
514 }
515
516 let mut idom: HashMap<BlockId, BlockId> = HashMap::new();
519 for &block in &block_list {
520 if block == self.entry {
521 continue;
522 }
523 if let Some(doms) = dom.get(&block) {
524 let mut best_candidate: Option<BlockId> = None;
526 let mut best_size = 0;
527
528 for &candidate in doms {
529 if candidate == block {
530 continue;
531 }
532 if let Some(candidate_doms) = dom.get(&candidate) {
533 if candidate_doms.len() > best_size {
534 best_size = candidate_doms.len();
535 best_candidate = Some(candidate);
536 }
537 }
538 }
539
540 if let Some(candidate) = best_candidate {
541 idom.insert(block, candidate);
542 }
543 }
544 }
545
546 DominatorTree {
547 root: self.entry,
548 dominators: idom,
549 }
550 }
551
552 pub fn detect_loops(&self) -> Vec<Loop> {
554 let dom = self.compute_dominators();
555 let mut loops = Vec::new();
556
557 for (from, tos) in &self.successors {
558 for to in tos {
559 if dom.dominates(*to, *from) {
560 let header = *to;
561 let mut loop_blocks = HashSet::new();
562 loop_blocks.insert(header);
563 let mut worklist = VecDeque::new();
564 worklist.push_back(*from);
565
566 while let Some(block) = worklist.pop_front() {
567 if loop_blocks.contains(&block) {
568 continue;
569 }
570 if dom.dominates(header, block) || block == header {
571 loop_blocks.insert(block);
572 if let Some(preds) = self.predecessors.get(&block) {
573 for &pred in preds {
574 if !loop_blocks.contains(&pred) {
575 worklist.push_back(pred);
576 }
577 }
578 }
579 }
580 }
581
582 let mut blocks: Vec<BlockId> = loop_blocks.into_iter().filter(|&b| b != header).collect();
583 blocks.sort();
584 loops.push(Loop::with_depth(header, blocks, 0));
585 }
586 }
587 }
588
589 loops
590 }
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596 use crate::storage::BackendKind;
597
598 #[tokio::test]
599 async fn test_cfg_module_creation() {
600 let store = Arc::new(UnifiedGraphStore::open(
601 tempfile::tempdir().unwrap().path(), BackendKind::SQLite
602 ).await.unwrap());
603 let module = CfgModule::new(store.clone());
604
605 assert_eq!(module.store.db_path(), store.db_path());
606 }
607
608 #[tokio::test]
609 async fn test_path_builder_filters() {
610 let store = Arc::new(UnifiedGraphStore::open(
611 std::env::current_dir().unwrap(),
612 BackendKind::SQLite
613 ).await.unwrap());
614
615 let dummy_module = CfgModule {
616 store: store.clone(),
617 };
618
619 let builder = dummy_module.paths(SymbolId(1))
620 .normal_only()
621 .max_length(10);
622
623 assert!(builder.normal_only);
624 assert_eq!(builder.max_length, Some(10));
625 }
626
627 #[tokio::test]
628 async fn test_dominators_basic() {
629 let store = Arc::new(UnifiedGraphStore::open(
630 tempfile::tempdir().unwrap().path(), BackendKind::SQLite
631 ).await.unwrap());
632 let module = CfgModule::new(store);
633
634 let doms = module.dominators(SymbolId(1)).await.unwrap();
635 assert_eq!(doms.root, BlockId(0));
636 assert_eq!(doms.dominators.len(), 1);
638 }
639
640 #[tokio::test]
641 async fn test_loops_empty() {
642 let store = Arc::new(UnifiedGraphStore::open(
643 tempfile::tempdir().unwrap().path(), BackendKind::SQLite
644 ).await.unwrap());
645 let module = CfgModule::new(store);
646
647 let loops = module.loops(SymbolId(1)).await.unwrap();
648 assert_eq!(loops.len(), 0);
649 }
650
651 #[tokio::test]
652 async fn test_paths_execute_returns_placeholder() {
653 let store = Arc::new(UnifiedGraphStore::open(
654 tempfile::tempdir().unwrap().path(), BackendKind::SQLite
655 ).await.unwrap());
656 let module = CfgModule::new(store);
657
658 let paths = module.paths(SymbolId(1)).execute().await.unwrap();
659 assert_eq!(paths.len(), 1);
661 assert_eq!(paths[0].blocks.len(), 1);
662 }
663
664 #[test]
665 fn test_dominator_tree_creation() {
666 let tree = DominatorTree::new(BlockId(0));
667 assert_eq!(tree.root, BlockId(0));
668 assert!(tree.is_empty());
669 assert_eq!(tree.len(), 1);
670 }
671
672 #[test]
673 fn test_dominator_tree_insert() {
674 let mut tree = DominatorTree::new(BlockId(0));
675 tree.insert(BlockId(1), BlockId(0));
676 tree.insert(BlockId(2), BlockId(1));
677
678 assert_eq!(tree.len(), 3);
679 assert_eq!(tree.immediate_dominator(BlockId(1)), Some(BlockId(0)));
680 assert_eq!(tree.immediate_dominator(BlockId(2)), Some(BlockId(1)));
681 assert_eq!(tree.immediate_dominator(BlockId(0)), None);
682 }
683
684 #[test]
685 fn test_dominator_tree_dominates() {
686 let mut tree = DominatorTree::new(BlockId(0));
687 tree.insert(BlockId(1), BlockId(0));
688 tree.insert(BlockId(2), BlockId(1));
689
690 assert!(tree.dominates(BlockId(0), BlockId(0)));
691 assert!(tree.dominates(BlockId(0), BlockId(1)));
692 assert!(tree.dominates(BlockId(0), BlockId(2)));
693 assert!(tree.dominates(BlockId(1), BlockId(1)));
694 assert!(!tree.dominates(BlockId(1), BlockId(0)));
695 }
696
697 #[test]
698 fn test_loop_creation() {
699 let loop_ = Loop::new(BlockId(1));
700 assert_eq!(loop_.header, BlockId(1));
701 assert!(loop_.is_empty());
702 assert_eq!(loop_.len(), 1);
703 assert_eq!(loop_.depth, 0);
704 }
705
706 #[test]
707 fn test_loop_with_blocks() {
708 let blocks = vec![BlockId(2), BlockId(3)];
709 let loop_ = Loop::with_blocks(BlockId(1), blocks.clone());
710
711 assert_eq!(loop_.header, BlockId(1));
712 assert_eq!(loop_.blocks, blocks);
713 assert!(!loop_.is_empty());
714 assert_eq!(loop_.len(), 3);
715 }
716
717 #[test]
718 fn test_loop_contains() {
719 let loop_ = Loop::with_blocks(BlockId(1), vec![BlockId(2), BlockId(3)]);
720
721 assert!(loop_.contains(BlockId(1)));
722 assert!(loop_.contains(BlockId(2)));
723 assert!(loop_.contains(BlockId(3)));
724 assert!(!loop_.contains(BlockId(4)));
725 }
726
727 #[test]
728 fn test_path_creation() {
729 let blocks = vec![BlockId(0), BlockId(1), BlockId(2)];
730 let path = Path::new(blocks.clone());
731
732 assert_eq!(path.blocks, blocks);
733 assert_eq!(path.length, 3);
734 assert!(path.is_normal());
735 assert!(!path.is_error());
736 }
737
738 #[test]
739 fn test_path_with_kind() {
740 let blocks = vec![BlockId(0), BlockId(1)];
741 let path = Path::with_kind(blocks.clone(), PathKind::Error);
742
743 assert_eq!(path.blocks, blocks);
744 assert_eq!(path.kind, PathKind::Error);
745 assert!(!path.is_normal());
746 assert!(path.is_error());
747 }
748
749 #[test]
750 fn test_path_contains() {
751 let path = Path::new(vec![BlockId(0), BlockId(1), BlockId(2)]);
752
753 assert!(path.contains(BlockId(0)));
754 assert!(path.contains(BlockId(1)));
755 assert!(path.contains(BlockId(2)));
756 assert!(!path.contains(BlockId(3)));
757 }
758
759 #[test]
760 fn test_path_entry_exit() {
761 let path = Path::new(vec![BlockId(0), BlockId(1), BlockId(2)]);
762
763 assert_eq!(path.entry(), Some(BlockId(0)));
764 assert_eq!(path.exit(), Some(BlockId(2)));
765 }
766
767 #[test]
768 fn test_path_id_stability() {
769 let blocks = vec![BlockId(0), BlockId(1), BlockId(2)];
770 let path1 = Path::new(blocks.clone());
771 let path2 = Path::new(blocks);
772
773 assert_eq!(path1.id, path2.id);
774 }
775
776 #[test]
777 fn test_path_id_uniqueness() {
778 let blocks1 = vec![BlockId(0), BlockId(1), BlockId(2)];
779 let blocks2 = vec![BlockId(0), BlockId(1), BlockId(3)];
780 let path1 = Path::new(blocks1);
781 let path2 = Path::new(blocks2);
782
783 assert_ne!(path1.id, path2.id);
784 }
785
786 #[test]
787 fn test_test_cfg_chain() {
788 let cfg = TestCfg::chain(0, 5);
789
790 assert_eq!(cfg.entry, BlockId(0));
791 assert!(cfg.exits.contains(&BlockId(4)));
792
793 assert_eq!(cfg.successors.get(&BlockId(0)), Some(&vec![BlockId(1)]));
795 assert_eq!(cfg.successors.get(&BlockId(1)), Some(&vec![BlockId(2)]));
796 assert_eq!(cfg.successors.get(&BlockId(2)), Some(&vec![BlockId(3)]));
797 assert_eq!(cfg.successors.get(&BlockId(3)), Some(&vec![BlockId(4)]));
798 }
799
800 #[test]
801 fn test_test_cfg_if_else() {
802 let cfg = TestCfg::if_else();
803
804 assert_eq!(cfg.entry, BlockId(0));
805 assert!(cfg.exits.contains(&BlockId(3)));
806
807 let succ0 = cfg.successors.get(&BlockId(0)).unwrap();
808 assert!(succ0.contains(&BlockId(1)));
809 assert!(succ0.contains(&BlockId(2)));
810 assert_eq!(cfg.successors.get(&BlockId(1)), Some(&vec![BlockId(3)]));
811 assert_eq!(cfg.successors.get(&BlockId(2)), Some(&vec![BlockId(3)]));
812 }
813
814 #[test]
815 fn test_paths_simple_chain() {
816 let cfg = TestCfg::chain(0, 4);
817 let paths = cfg.enumerate_paths();
818
819 assert_eq!(paths.len(), 1);
820 assert_eq!(paths[0].blocks, vec![BlockId(0), BlockId(1), BlockId(2), BlockId(3)]);
821 assert!(paths[0].is_normal());
822 }
823
824 #[test]
825 fn test_paths_if_else() {
826 let cfg = TestCfg::if_else();
827 let paths = cfg.enumerate_paths();
828
829 assert_eq!(paths.len(), 2);
830
831 assert_eq!(paths[0].entry(), Some(BlockId(0)));
832 assert_eq!(paths[0].exit(), Some(BlockId(3)));
833 assert_eq!(paths[1].entry(), Some(BlockId(0)));
834 assert_eq!(paths[1].exit(), Some(BlockId(3)));
835
836 let paths_set: HashSet<_> = paths.iter().map(|p| p.blocks.clone()).collect();
837
838 assert!(paths_set.contains(&vec![BlockId(0), BlockId(1), BlockId(3)]));
839 assert!(paths_set.contains(&vec![BlockId(0), BlockId(2), BlockId(3)]));
840 }
841
842 #[test]
843 fn test_dominators_chain() {
844 let cfg = TestCfg::chain(0, 5);
845 let dom = cfg.compute_dominators();
846
847 assert_eq!(dom.root, BlockId(0));
848 assert_eq!(dom.immediate_dominator(BlockId(1)), Some(BlockId(0)));
849 assert_eq!(dom.immediate_dominator(BlockId(2)), Some(BlockId(1)));
850 assert_eq!(dom.immediate_dominator(BlockId(3)), Some(BlockId(2)));
851 assert_eq!(dom.immediate_dominator(BlockId(4)), Some(BlockId(3)));
852 }
853
854 #[test]
855 fn test_dominators_if_else() {
856 let cfg = TestCfg::if_else();
857 let dom = cfg.compute_dominators();
858
859 assert!(dom.dominates(BlockId(0), BlockId(0)));
860 assert!(dom.dominates(BlockId(0), BlockId(1)));
861 assert!(dom.dominates(BlockId(0), BlockId(2)));
862 assert!(dom.dominates(BlockId(0), BlockId(3)));
863 assert_eq!(dom.immediate_dominator(BlockId(3)), Some(BlockId(0)));
864 }
865
866 #[test]
867 fn test_loops_simple_loop() {
868 let cfg = TestCfg::simple_loop();
869 let loops = cfg.detect_loops();
870
871 assert_eq!(loops.len(), 1);
872 assert_eq!(loops[0].header, BlockId(1));
873 assert!(!loops[0].blocks.is_empty());
874 assert!(loops[0].blocks.contains(&BlockId(2)));
875 }
876
877 #[test]
878 fn test_loops_none_in_chain() {
879 let cfg = TestCfg::chain(0, 5);
880 let loops = cfg.detect_loops();
881
882 assert_eq!(loops.len(), 0);
883 }
884
885 #[test]
886 fn test_loops_none_in_if_else() {
887 let cfg = TestCfg::if_else();
888 let loops = cfg.detect_loops();
889
890 assert_eq!(loops.len(), 0);
891 }
892}