llvm_ir_analysis/
control_flow_graph.rs1use llvm_ir::{Function, Name, Terminator};
2use petgraph::prelude::{DiGraphMap, Direction};
3use std::fmt;
4
5pub struct ControlFlowGraph<'m> {
11 pub(crate) graph: DiGraphMap<CFGNode<'m>, ()>,
17
18 pub(crate) entry_node: CFGNode<'m>,
20}
21
22#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
24pub enum CFGNode<'m> {
25 Block(&'m Name),
27 Return,
29}
30
31impl<'m> fmt::Display for CFGNode<'m> {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 match self {
34 CFGNode::Block(block) => write!(f, "{}", block),
35 CFGNode::Return => write!(f, "Return"),
36 }
37 }
38}
39
40impl<'m> ControlFlowGraph<'m> {
41 pub(crate) fn new(function: &'m Function) -> Self {
42 let mut graph: DiGraphMap<CFGNode<'m>, ()> = DiGraphMap::with_capacity(
43 function.basic_blocks.len() + 1,
44 2 * function.basic_blocks.len(), );
46
47 for bb in &function.basic_blocks {
48 match &bb.term {
49 Terminator::Br(br) => {
50 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(&br.dest), ());
51 }
52 Terminator::CondBr(condbr) => {
53 graph.add_edge(
54 CFGNode::Block(&bb.name),
55 CFGNode::Block(&condbr.true_dest),
56 (),
57 );
58 graph.add_edge(
59 CFGNode::Block(&bb.name),
60 CFGNode::Block(&condbr.false_dest),
61 (),
62 );
63 }
64 Terminator::IndirectBr(ibr) => {
65 for dest in &ibr.possible_dests {
66 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(dest), ());
67 }
68 }
69 Terminator::Switch(switch) => {
70 graph.add_edge(
71 CFGNode::Block(&bb.name),
72 CFGNode::Block(&switch.default_dest),
73 (),
74 );
75 for (_, dest) in &switch.dests {
76 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(dest), ());
77 }
78 }
79 Terminator::Ret(_) | Terminator::Resume(_) => {
80 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Return, ());
81 }
82 Terminator::Invoke(invoke) => {
83 graph.add_edge(
84 CFGNode::Block(&bb.name),
85 CFGNode::Block(&invoke.return_label),
86 (),
87 );
88 graph.add_edge(
89 CFGNode::Block(&bb.name),
90 CFGNode::Block(&invoke.exception_label),
91 (),
92 );
93 }
94 Terminator::CleanupRet(cleanupret) => {
95 if let Some(dest) = &cleanupret.unwind_dest {
96 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(dest), ());
97 } else {
98 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Return, ());
99 }
100 }
101 Terminator::CatchRet(catchret) => {
102 graph.add_edge(
104 CFGNode::Block(&bb.name),
105 CFGNode::Block(&catchret.successor),
106 (),
107 );
108 }
109 Terminator::CatchSwitch(catchswitch) => {
110 if let Some(dest) = &catchswitch.default_unwind_dest {
111 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(dest), ());
112 } else {
113 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Return, ());
114 }
115 for handler in &catchswitch.catch_handlers {
116 graph.add_edge(CFGNode::Block(&bb.name), CFGNode::Block(handler), ());
117 }
118 }
119 Terminator::CallBr(_) => unimplemented!("CallBr instruction"),
120 Terminator::Unreachable(_) => {
121 }
123 }
124 }
125
126 Self {
127 graph,
128 entry_node: CFGNode::Block(&function.basic_blocks[0].name),
129 }
130 }
131
132 pub fn preds<'s>(&'s self, block: &'m Name) -> impl Iterator<Item = &'m Name> + 's {
134 self.preds_of_cfgnode(CFGNode::Block(block))
135 }
136
137 pub fn preds_of_return<'s>(&'s self) -> impl Iterator<Item = &'m Name> + 's {
140 self.preds_of_cfgnode(CFGNode::Return)
141 }
142
143 pub(crate) fn preds_of_cfgnode<'s>(
144 &'s self,
145 node: CFGNode<'m>,
146 ) -> impl Iterator<Item = &'m Name> + 's {
147 self.preds_as_nodes(node).map(|cfgnode| match cfgnode {
148 CFGNode::Block(block) => block,
149 CFGNode::Return => panic!("Shouldn't have CFGNode::Return as a predecessor"), })
151 }
152
153 pub(crate) fn preds_as_nodes<'s>(
154 &'s self,
155 node: CFGNode<'m>,
156 ) -> impl Iterator<Item = CFGNode<'m>> + 's {
157 self.graph.neighbors_directed(node, Direction::Incoming)
158 }
159
160 pub fn succs<'s>(&'s self, block: &'m Name) -> impl Iterator<Item = CFGNode<'m>> + 's {
164 self.graph
165 .neighbors_directed(CFGNode::Block(block), Direction::Outgoing)
166 }
167
168 pub fn entry(&self) -> &'m Name {
170 match self.entry_node {
171 CFGNode::Block(block) => block,
172 CFGNode::Return => panic!("Return node should not be entry"), }
174 }
175
176 pub(crate) fn reversed(&self) -> Self {
178 Self {
179 graph: DiGraphMap::from_edges(self.graph.all_edges().map(|(a, b, _)| (b, a, ()))),
180 entry_node: CFGNode::Return,
181 }
182 }
183}