1use std::collections::HashMap;
85
86use rucc_base::Symbol;
87use rucc_ir::{AliasKind, Datum, Extra, Func, FuncId, Inst, Linkage, Module, Opcode, Pic};
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
94pub struct Node(u32);
95
96impl Node {
97 #[must_use]
99 pub const fn index(self) -> usize {
100 self.0 as usize
101 }
102}
103
104#[derive(Debug, Clone)]
106struct Entry {
107 name: Symbol,
109 func: Option<FuncId>,
112 body: Option<FuncId>,
114 calls: Vec<Node>,
116 unknown: bool,
118 address_taken: bool,
120}
121
122#[derive(Debug, Clone, Default)]
127pub struct CallGraph {
128 entries: Vec<Entry>,
130 by_name: HashMap<Symbol, Node>,
132 components: Vec<Vec<Node>>,
134 component_of: Vec<u32>,
136}
137
138impl CallGraph {
139 #[must_use]
144 pub fn of(module: &Module, pic: Pic) -> Self {
145 let mut graph = Self::default();
146 for id in module.funcs() {
147 let func = &module[id];
148 let body = (!func.is_declaration() && trusted(func, pic)).then_some(id);
149 let node = graph.intern(func.name);
150 let at = node.index();
151 graph.entries[at].func = Some(id);
152 graph.entries[at].body = body;
153 graph.entries[at].unknown = body.is_none();
156 }
157 for id in module.aliases() {
160 let alias = &module[id];
161 let node = graph.intern(alias.name);
162 match alias.kind {
163 AliasKind::Alias => {
166 let to = graph.intern(alias.target);
167 graph.entries[node.index()].calls.push(to);
168 }
171 AliasKind::IFunc => {
175 graph.entries[node.index()].unknown = true;
176 let resolver = graph.intern(alias.target);
177 graph.entries[resolver.index()].address_taken = true;
178 }
179 }
180 }
181 for id in module.funcs() {
184 let func = &module[id];
185 if func.is_declaration() {
186 continue;
187 }
188 let from = graph.by_name[&func.name];
189 for block in func.blocks() {
190 for inst in func.insts(block) {
191 graph.read(func, inst, from);
192 }
193 }
194 }
195 for id in module.globals() {
198 let init = module[id].init.map(|list| &module[list]).unwrap_or_default();
199 for datum in init {
200 if let Datum::Addr(reloc) = *datum {
201 graph.took_the_address_of(module[reloc].symbol);
202 }
203 }
204 }
205 graph.condense();
206 graph
207 }
208
209 pub fn nodes(&self) -> impl Iterator<Item = Node> + use<> {
211 (0..self.entries.len() as u32).map(Node)
212 }
213
214 #[must_use]
216 pub fn len(&self) -> usize {
217 self.entries.len()
218 }
219
220 #[must_use]
222 pub fn is_empty(&self) -> bool {
223 self.entries.is_empty()
224 }
225
226 #[must_use]
228 pub fn node(&self, name: Symbol) -> Option<Node> {
229 self.by_name.get(&name).copied()
230 }
231
232 #[must_use]
234 pub fn name(&self, node: Node) -> Symbol {
235 self.entries[node.index()].name
236 }
237
238 #[must_use]
244 pub fn func(&self, node: Node) -> Option<FuncId> {
245 self.entries[node.index()].func
246 }
247
248 #[must_use]
256 pub fn trusted_body(&self, node: Node) -> Option<FuncId> {
257 self.entries[node.index()].body
258 }
259
260 #[must_use]
262 pub fn calls(&self, node: Node) -> &[Node] {
263 &self.entries[node.index()].calls
264 }
265
266 #[must_use]
273 pub fn reaches_unknown(&self, node: Node) -> bool {
274 self.entries[node.index()].unknown
275 }
276
277 #[must_use]
288 pub fn address_taken(&self, node: Node) -> bool {
289 self.entries[node.index()].address_taken
290 }
291
292 #[must_use]
299 pub fn components(&self) -> &[Vec<Node>] {
300 &self.components
301 }
302
303 #[must_use]
305 pub fn component_of(&self, node: Node) -> usize {
306 self.component_of[node.index()] as usize
307 }
308
309 pub fn solve<T, S, F>(&self, start: S, mut transfer: F) -> Vec<T>
332 where
333 T: Clone + PartialEq,
334 S: Fn(Node) -> T,
335 F: FnMut(Node, &[T]) -> T,
336 {
337 let mut answers: Vec<T> = self.nodes().map(&start).collect();
338 for part in &self.components {
339 if let [only] = part[..] {
340 if !self.entries[only.index()].calls.contains(&only) {
341 answers[only.index()] = transfer(only, &answers);
342 continue;
343 }
344 }
345 let ceiling = 1000 + part.len() * 64;
350 let mut rounds = 0usize;
351 loop {
352 let mut settled = true;
353 for &node in part {
354 let now = transfer(node, &answers);
355 if now != answers[node.index()] {
356 answers[node.index()] = now;
357 settled = false;
358 }
359 }
360 if settled {
361 break;
362 }
363 rounds += 1;
364 debug_assert!(rounds < ceiling, "the transfer function is not monotone");
365 }
366 }
367 answers
368 }
369
370 fn intern(&mut self, name: Symbol) -> Node {
372 if let Some(&node) = self.by_name.get(&name) {
373 return node;
374 }
375 let node = Node(self.entries.len() as u32);
376 self.entries.push(Entry {
380 name,
381 func: None,
382 body: None,
383 calls: Vec::new(),
384 unknown: true,
385 address_taken: false,
386 });
387 self.by_name.insert(name, node);
388 node
389 }
390
391 fn took_the_address_of(&mut self, name: Symbol) {
402 if let Some(&node) = self.by_name.get(&name) {
403 self.entries[node.index()].address_taken = true;
404 }
405 }
406
407 fn read(&mut self, func: &Func, inst: Inst, from: Node) {
409 let data = &func[inst];
410 match data.opcode {
411 Opcode::Call | Opcode::TailCall => {
412 let name = match data.extra {
413 Extra::Call(at) => func[at].callee,
414 _ => None,
415 };
416 let Some(name) = name else {
419 self.entries[from.index()].unknown = true;
420 return;
421 };
422 let to = self.intern(name);
423 let calls = &mut self.entries[from.index()].calls;
424 if !calls.contains(&to) {
425 calls.push(to);
426 }
427 }
428 Opcode::CallIndirect => self.entries[from.index()].unknown = true,
431 Opcode::InlineAsm | Opcode::TargetIntrinsic => {
435 self.entries[from.index()].unknown = true;
436 }
437 Opcode::GlobalAddr => {
439 if let Extra::Symbol(name) = data.extra {
440 self.took_the_address_of(name);
441 }
442 }
443 _ => {}
444 }
445 }
446
447 fn condense(&mut self) {
453 let count = self.entries.len();
454 let mut index = vec![u32::MAX; count];
457 let mut low = vec![0u32; count];
458 let mut on_stack = vec![false; count];
459 let mut stack: Vec<u32> = Vec::new();
460 let mut frames: Vec<(u32, usize)> = Vec::new();
461 let mut next = 0u32;
462 self.component_of = vec![u32::MAX; count];
463 for root in 0..count as u32 {
464 if index[root as usize] != u32::MAX {
465 continue;
466 }
467 index[root as usize] = next;
468 low[root as usize] = next;
469 next += 1;
470 stack.push(root);
471 on_stack[root as usize] = true;
472 frames.push((root, 0));
473 while let Some(&(node, at)) = frames.last() {
474 let edges = &self.entries[node as usize].calls;
475 if at < edges.len() {
476 let to = edges[at].0;
477 frames.last_mut().expect("the frame just read").1 += 1;
478 if index[to as usize] == u32::MAX {
479 index[to as usize] = next;
480 low[to as usize] = next;
481 next += 1;
482 stack.push(to);
483 on_stack[to as usize] = true;
484 frames.push((to, 0));
485 } else if on_stack[to as usize] {
486 low[node as usize] = low[node as usize].min(index[to as usize]);
487 }
488 continue;
489 }
490 frames.pop();
491 if low[node as usize] == index[node as usize] {
492 let mut part = Vec::new();
493 while let Some(top) = stack.pop() {
494 on_stack[top as usize] = false;
495 part.push(Node(top));
496 if top == node {
497 break;
498 }
499 }
500 part.sort_unstable();
501 let which = self.components.len() as u32;
502 for member in &part {
503 self.component_of[member.index()] = which;
504 }
505 self.components.push(part);
506 }
507 if let Some(&(above, _)) = frames.last() {
508 low[above as usize] = low[above as usize].min(low[node as usize]);
509 }
510 }
511 }
512 debug_assert!(
513 self.component_of.iter().all(|&which| which != u32::MAX),
514 "every node is in a component"
515 );
516 }
517}
518
519fn trusted(func: &Func, pic: Pic) -> bool {
529 !matches!(func.linkage, Linkage::Weak | Linkage::Common)
530 && !pic.replaceable(func.linkage, func.visibility)
531}
532
533#[cfg(test)]
534mod tests {
535 use rucc_base::Interner;
536 use rucc_ir::{
537 Alias, AliasKind, AsmInfo, BlockCallList, Builder, CallInfo, Datum, Extra, Flags, Func,
538 Global, InstData, Linkage, Module, Opcode, Pic, Reloc, Signature, Type, Visibility,
539 };
540 use rucc_target::{TargetInfo, Triple};
541
542 use super::{CallGraph, Node};
543
544 fn blank() -> (Interner, Module) {
546 let mut names = Interner::new();
547 let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
548 let module = Module::new(names.intern("t.c"), &target);
549 (names, module)
550 }
551
552 fn calling(names: &mut Interner, module: &mut Module, name: &str, callees: &[&str]) {
554 let mut func = Func::new(names.intern(name), Signature::new());
555 let block = func.create_block();
556 let mut build = Builder::new(&mut func, block);
557 let signature = build.func().add_signature(Signature::new());
558 for callee in callees {
559 build.call(names.intern(callee), signature, &[]);
560 }
561 build.ret(&[]);
562 module.add_func(func);
563 }
564
565 fn declaring(names: &mut Interner, module: &mut Module, name: &str) {
567 module.add_func(Func::new(names.intern(name), Signature::new()));
568 }
569
570 fn node(graph: &CallGraph, names: &mut Interner, name: &str) -> Node {
572 let name = names.intern(name);
573 graph.node(name).unwrap_or_else(|| panic!("no node for {}", names.resolve(name)))
574 }
575
576 fn order(graph: &CallGraph, names: &Interner) -> Vec<Vec<String>> {
578 graph
579 .components()
580 .iter()
581 .map(|part| part.iter().map(|&it| names.resolve(graph.name(it)).to_string()).collect())
582 .collect()
583 }
584
585 #[test]
586 fn a_call_is_an_edge_and_the_callee_gets_a_node_of_its_own() {
587 let (mut names, mut module) = blank();
588 calling(&mut names, &mut module, "f", &["g"]);
589 calling(&mut names, &mut module, "g", &[]);
590 let graph = CallGraph::of(&module, Pic::Executable);
591 let (f, g) = (node(&graph, &mut names, "f"), node(&graph, &mut names, "g"));
592 assert_eq!(graph.calls(f), [g]);
593 assert_eq!(graph.calls(g), []);
594 }
595
596 #[test]
597 fn the_same_callee_twice_is_one_edge() {
598 let (mut names, mut module) = blank();
599 calling(&mut names, &mut module, "f", &["g", "h", "g"]);
600 calling(&mut names, &mut module, "g", &[]);
601 calling(&mut names, &mut module, "h", &[]);
602 let graph = CallGraph::of(&module, Pic::Executable);
603 let f = node(&graph, &mut names, "f");
604 let (g, h) = (node(&graph, &mut names, "g"), node(&graph, &mut names, "h"));
605 assert_eq!(graph.calls(f), [g, h], "the order the body calls them, each once");
606 }
607
608 #[test]
609 fn a_name_the_module_never_declared_still_gets_a_node() {
610 let (mut names, mut module) = blank();
611 calling(&mut names, &mut module, "f", &["witness"]);
612 let graph = CallGraph::of(&module, Pic::Executable);
613 let witness = node(&graph, &mut names, "witness");
614 assert_eq!(graph.calls(node(&graph, &mut names, "f")), [witness]);
615 assert_eq!(graph.func(witness), None);
616 assert_eq!(graph.trusted_body(witness), None);
617 assert!(graph.reaches_unknown(witness), "its body is somewhere this graph cannot see");
618 }
619
620 #[test]
621 fn a_declaration_has_a_function_and_no_body_and_reaches_the_unknown() {
622 let (mut names, mut module) = blank();
623 declaring(&mut names, &mut module, "printf");
624 let graph = CallGraph::of(&module, Pic::Executable);
625 let printf = node(&graph, &mut names, "printf");
626 assert!(graph.func(printf).is_some());
627 assert_eq!(graph.trusted_body(printf), None);
628 assert!(graph.reaches_unknown(printf));
629 }
630
631 #[test]
632 fn a_body_this_link_will_keep_is_one_an_analysis_may_read() {
633 let (mut names, mut module) = blank();
634 calling(&mut names, &mut module, "f", &[]);
635 let graph = CallGraph::of(&module, Pic::Executable);
636 let f = node(&graph, &mut names, "f");
637 assert!(graph.trusted_body(f).is_some());
638 assert!(!graph.reaches_unknown(f));
639 }
640
641 #[test]
642 fn a_weak_definition_is_not_a_body_this_analysis_may_read() {
643 let (mut names, mut module) = blank();
644 calling(&mut names, &mut module, "f", &[]);
645 let id = module.funcs().next().expect("the one function");
646 module[id].linkage = Linkage::Weak;
647 let graph = CallGraph::of(&module, Pic::Executable);
648 let f = node(&graph, &mut names, "f");
649 assert!(graph.func(f).is_some(), "the declaration is still there");
650 assert_eq!(graph.trusted_body(f), None, "another object may win over it");
651 assert!(graph.reaches_unknown(f));
652 }
653
654 #[test]
655 fn an_exported_definition_in_a_library_may_be_interposed_and_a_hidden_one_may_not() {
656 let (mut names, mut module) = blank();
657 calling(&mut names, &mut module, "f", &[]);
658 let id = module.funcs().next().expect("the one function");
659 let graph = CallGraph::of(&module, Pic::Library);
660 assert_eq!(graph.trusted_body(node(&graph, &mut names, "f")), None);
661 module[id].visibility = Visibility::Hidden;
662 let graph = CallGraph::of(&module, Pic::Library);
663 assert!(graph.trusted_body(node(&graph, &mut names, "f")).is_some());
664 }
665
666 #[test]
667 fn a_call_through_an_address_is_the_flag_and_not_an_edge() {
668 let (mut names, mut module) = blank();
669 let mut func = Func::new(names.intern("f"), Signature::new());
670 let block = func.create_block();
671 let mut build = Builder::new(&mut func, block);
672 let extra = Extra::Symbol(names.intern("g"));
673 let target =
674 build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
675 let signature = build.func().add_signature(Signature::new());
676 let varargs = build.func().push_abis(&[]);
677 let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
678 let args = build.func().push_values(&[target]);
679 build.inst(
680 InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
681 &[],
682 );
683 build.ret(&[]);
684 module.add_func(func);
685 calling(&mut names, &mut module, "g", &[]);
686 let graph = CallGraph::of(&module, Pic::Executable);
687 let f = node(&graph, &mut names, "f");
688 assert_eq!(graph.calls(f), [], "nothing here names what is at the other end");
689 assert!(graph.reaches_unknown(f));
690 assert!(graph.address_taken(node(&graph, &mut names, "g")));
691 }
692
693 #[test]
694 fn a_function_named_in_an_image_has_had_its_address_taken() {
695 let (mut names, mut module) = blank();
696 calling(&mut names, &mut module, "handler", &[]);
697 let handler = names.intern("handler");
698 let reloc = module.add_reloc(Reloc { symbol: handler, addend: 0, size: 8 });
699 let mut table = Global::new(names.intern("table"), 8, 8);
700 table.init = Some(module.push_data(&[Datum::Addr(reloc)]));
701 module.add_global(table);
702 let graph = CallGraph::of(&module, Pic::Executable);
703 assert!(graph.address_taken(node(&graph, &mut names, "handler")));
704 }
705
706 #[test]
707 fn taking_the_address_of_a_variable_puts_nothing_in_the_graph() {
708 let (mut names, mut module) = blank();
709 let mut func = Func::new(names.intern("f"), Signature::new());
710 let block = func.create_block();
711 let mut build = Builder::new(&mut func, block);
712 let extra = Extra::Symbol(names.intern("counter"));
713 build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
714 build.ret(&[]);
715 module.add_func(func);
716 module.add_global(Global::new(names.intern("counter"), 4, 4));
717 let graph = CallGraph::of(&module, Pic::Executable);
718 assert_eq!(graph.len(), 1, "the one function and nothing else");
719 assert_eq!(graph.node(names.intern("counter")), None);
720 }
721
722 #[test]
723 fn an_alias_is_an_edge_to_what_it_aliases() {
724 let (mut names, mut module) = blank();
725 calling(&mut names, &mut module, "caller", &["shorthand"]);
726 calling(&mut names, &mut module, "real", &[]);
727 module.add_alias(Alias::new(names.intern("shorthand"), names.intern("real")));
728 let graph = CallGraph::of(&module, Pic::Executable);
729 let shorthand = node(&graph, &mut names, "shorthand");
730 let real = node(&graph, &mut names, "real");
731 assert_eq!(graph.calls(node(&graph, &mut names, "caller")), [shorthand]);
732 assert_eq!(graph.calls(shorthand), [real], "a call to the alias is a call to the body");
733 }
734
735 #[test]
736 fn an_ifunc_reaches_the_unknown_and_its_resolver_has_had_its_address_taken() {
737 let (mut names, mut module) = blank();
738 calling(&mut names, &mut module, "resolve", &[]);
739 let mut alias = Alias::new(names.intern("memcpy"), names.intern("resolve"));
740 alias.kind = AliasKind::IFunc;
741 module.add_alias(alias);
742 let graph = CallGraph::of(&module, Pic::Executable);
743 let memcpy = node(&graph, &mut names, "memcpy");
744 assert_eq!(graph.calls(memcpy), [], "what it resolves to is not a name this module has");
745 assert!(graph.reaches_unknown(memcpy));
746 assert!(graph.address_taken(node(&graph, &mut names, "resolve")));
747 assert!(!graph.address_taken(memcpy));
748 }
749
750 #[test]
751 fn inline_assembly_reaches_the_unknown() {
752 let (mut names, mut module) = blank();
753 let mut func = Func::new(names.intern("f"), Signature::new());
754 let block = func.create_block();
755 let mut build = Builder::new(&mut func, block);
756 build.inline_asm(
757 AsmInfo {
758 template: names.intern("nop"),
759 constraints: names.intern(""),
760 clobbers: names.intern(""),
761 targets: BlockCallList::EMPTY,
762 },
763 &[],
764 &[],
765 Flags::NONE,
766 );
767 build.ret(&[]);
768 module.add_func(func);
769 let graph = CallGraph::of(&module, Pic::Executable);
770 assert!(graph.reaches_unknown(node(&graph, &mut names, "f")));
771 }
772
773 #[test]
774 fn a_target_intrinsic_reaches_the_unknown() {
775 let (mut names, mut module) = blank();
776 let mut func = Func::new(names.intern("f"), Signature::new());
777 let block = func.create_block();
778 let mut build = Builder::new(&mut func, block);
779 let extra = Extra::Symbol(names.intern("x86.pause"));
780 build.inst(InstData { extra, ..InstData::new(Opcode::TargetIntrinsic) }, &[]);
781 build.ret(&[]);
782 module.add_func(func);
783 let graph = CallGraph::of(&module, Pic::Executable);
784 assert!(graph.reaches_unknown(node(&graph, &mut names, "f")));
785 }
786
787 #[test]
788 fn a_chain_of_callers_comes_out_callee_before_caller() {
789 let (mut names, mut module) = blank();
790 calling(&mut names, &mut module, "top", &["middle"]);
791 calling(&mut names, &mut module, "middle", &["bottom"]);
792 calling(&mut names, &mut module, "bottom", &[]);
793 let graph = CallGraph::of(&module, Pic::Executable);
794 assert_eq!(order(&graph, &names), [["bottom"], ["middle"], ["top"]]);
795 }
796
797 #[test]
798 fn a_function_that_calls_itself_is_a_component_of_one_that_is_a_cycle() {
799 let (mut names, mut module) = blank();
800 calling(&mut names, &mut module, "spin", &["spin"]);
801 let graph = CallGraph::of(&module, Pic::Executable);
802 let spin = node(&graph, &mut names, "spin");
803 assert_eq!(graph.calls(spin), [spin]);
804 assert_eq!(order(&graph, &names), [["spin"]]);
805 }
806
807 #[test]
808 fn two_functions_that_call_each_other_are_one_component() {
809 let (mut names, mut module) = blank();
810 calling(&mut names, &mut module, "even", &["odd"]);
811 calling(&mut names, &mut module, "odd", &["even"]);
812 calling(&mut names, &mut module, "main", &["even"]);
813 let graph = CallGraph::of(&module, Pic::Executable);
814 assert_eq!(order(&graph, &names), [vec!["even", "odd"], vec!["main"]]);
815 let (even, odd) = (node(&graph, &mut names, "even"), node(&graph, &mut names, "odd"));
816 assert_eq!(graph.component_of(even), graph.component_of(odd));
817 }
818
819 #[test]
820 fn a_component_holds_its_nodes_in_the_graphs_own_order() {
821 let (mut names, mut module) = blank();
822 calling(&mut names, &mut module, "a", &["c"]);
825 calling(&mut names, &mut module, "b", &["a"]);
826 calling(&mut names, &mut module, "c", &["b"]);
827 let graph = CallGraph::of(&module, Pic::Executable);
828 assert_eq!(order(&graph, &names), [["a", "b", "c"]]);
829 let a = node(&graph, &mut names, "a");
830 assert_eq!(graph.components()[graph.component_of(a)][0], a);
831 }
832
833 #[test]
834 fn the_walk_settles_a_component_before_anything_that_calls_into_it() {
835 let (mut names, mut module) = blank();
836 calling(&mut names, &mut module, "top", &["middle"]);
837 calling(&mut names, &mut module, "middle", &["bottom"]);
838 calling(&mut names, &mut module, "bottom", &[]);
839 let graph = CallGraph::of(&module, Pic::Executable);
840 let depth = graph.solve(
843 |_| 0usize,
844 |node, answers| {
845 graph.calls(node).iter().map(|&it| answers[it.index()] + 1).max().unwrap_or(0)
846 },
847 );
848 assert_eq!(depth[node(&graph, &mut names, "bottom").index()], 0);
849 assert_eq!(depth[node(&graph, &mut names, "middle").index()], 1);
850 assert_eq!(depth[node(&graph, &mut names, "top").index()], 2);
851 }
852
853 #[test]
854 fn a_component_of_one_with_no_edge_to_itself_is_asked_once() {
855 let (mut names, mut module) = blank();
856 calling(&mut names, &mut module, "f", &["g"]);
857 calling(&mut names, &mut module, "g", &[]);
858 let graph = CallGraph::of(&module, Pic::Executable);
859 let mut asked = 0usize;
860 let answers: Vec<bool> = graph.solve(
861 |_| false,
862 |_, _| {
863 asked += 1;
864 true
865 },
866 );
867 assert_eq!(asked, 2, "one question each, with nothing to settle");
868 assert!(answers[node(&graph, &mut names, "f").index()]);
869 }
870
871 #[test]
872 fn a_cycle_is_iterated_until_nothing_moves() {
873 let (mut names, mut module) = blank();
874 calling(&mut names, &mut module, "even", &["odd"]);
875 calling(&mut names, &mut module, "odd", &["even"]);
876 let graph = CallGraph::of(&module, Pic::Executable);
877 let odd = node(&graph, &mut names, "odd");
880 let settled = graph.solve(
881 |_| true,
882 |node, answers| node != odd && graph.calls(node).iter().all(|&it| answers[it.index()]),
883 );
884 assert!(!settled[odd.index()]);
885 assert!(!settled[node(&graph, &mut names, "even").index()], "through the cycle");
886 }
887
888 #[test]
889 fn an_empty_module_is_an_empty_graph() {
890 let (_, module) = blank();
891 let graph = CallGraph::of(&module, Pic::Executable);
892 assert!(graph.is_empty());
893 assert_eq!(graph.len(), 0);
894 assert!(graph.components().is_empty());
895 let answers: Vec<usize> = graph.solve(|_| 0, |_, _| 0);
896 assert!(answers.is_empty());
897 }
898
899 #[test]
900 fn the_graph_is_the_same_graph_every_time_it_is_built() {
901 let (mut names, mut module) = blank();
902 for name in ["one", "two", "three", "four", "five"] {
903 calling(&mut names, &mut module, name, &["helper", "one"]);
904 }
905 calling(&mut names, &mut module, "helper", &[]);
906 let first = CallGraph::of(&module, Pic::Executable);
907 let spelling = |graph: &CallGraph| {
908 graph
909 .nodes()
910 .map(|it| {
911 let calls: Vec<&str> =
912 graph.calls(it).iter().map(|&to| names.resolve(graph.name(to))).collect();
913 (names.resolve(graph.name(it)).to_string(), calls.join(" "))
914 })
915 .collect::<Vec<_>>()
916 };
917 for _ in 0..8 {
918 let again = CallGraph::of(&module, Pic::Executable);
919 assert_eq!(spelling(&first), spelling(&again));
920 assert_eq!(order(&first, &names), order(&again, &names));
921 }
922 }
923
924 #[test]
925 fn a_chain_deeper_than_a_recursive_walk_could_manage_still_comes_out_in_order() {
926 let (mut names, mut module) = blank();
927 let deep = 20_000;
928 for at in 0..deep {
929 let next = format!("f{}", at + 1);
930 calling(&mut names, &mut module, &format!("f{at}"), &[next.as_str()]);
931 }
932 let graph = CallGraph::of(&module, Pic::Executable);
933 assert_eq!(graph.components().len(), deep + 1, "the tail name gets one of its own");
934 let top = node(&graph, &mut names, "f0");
935 assert_eq!(graph.component_of(top), deep, "settled last, after everything under it");
936 }
937}