1use std::collections::HashMap;
2use crate::types::*;
3pub mod types;
4
5pub struct ControlFlowGraph {
6 current_block: usize,
8 blocks: Vec<BasicBlock>
10}
11
12impl ControlFlowGraph {
13 fn new(entry_point: usize) -> Self {
15 ControlFlowGraph { current_block: 0, blocks: vec![BasicBlock::new(entry_point)] }
16 }
17
18 fn add_edge(&mut self, src_block: usize, dest_block: usize, traversed: bool) -> Result<(), CFGError> {
20 let src_block = self.blocks.get_mut(src_block).ok_or(CFGError::MissingBlock)?;
21 src_block.add_edge(dest_block, traversed);
22 Ok(())
23 }
24
25 fn add_block(&mut self, block: BasicBlock) -> usize {
27 self.blocks.push(block);
28 self.blocks.len() - 1
29 }
30
31 fn query_block_or_create(&mut self, address: usize) -> usize {
33 self.blocks.iter().position(|bb| bb.start == address).unwrap_or_else(|| { let new_block = BasicBlock::new(address); self.add_block(new_block) } )
34 }
35
36 pub fn blocks(&self) -> impl Iterator<Item=&BasicBlock> {
38 self.blocks.iter()
39 }
40
41 pub fn execute(&mut self, program_counter: usize, instruction: BlockType) -> Result<(), CFGError> {
43 match instruction {
44 BlockType::Instruction(name, operand) => {
45 let curr_block = self.blocks.get_mut(self.current_block).ok_or(CFGError::MissingCurrentBlock)?;
46 assert!(program_counter >= curr_block.start, "Attempted to add an instruction behind the starting of the current block."); if !curr_block.block.contains_key(&program_counter) {
48 curr_block.add_instruction(program_counter, BlockType::Instruction(name, operand));
49 }
50
51 Ok(())
52 }
53 BlockType::Jump(name, success_address, jump_type, failure_address) => {
54 let curr_block = self.blocks.get_mut(self.current_block).ok_or(CFGError::MissingCurrentBlock)?;
56 assert!(program_counter >= curr_block.start, "Attempted to add an instruction behind the starting of the current block.");
57 if !curr_block.block.contains_key(&program_counter) {
58 curr_block.add_instruction(program_counter, BlockType::Jump(name, success_address, jump_type, failure_address));
60 }
61 match jump_type {
62 JumpType::UnconditionalJump => {
63 let success_index = self.query_block_or_create(success_address);
64 self.add_edge(self.current_block, success_index, true)?;
65 self.current_block = success_index;
66 Ok(())
67 }
68 JumpType::ConditionalTaken => {
69 let failure_address = failure_address.ok_or(CFGError::ExpectedFailureAddress)?;
71
72 let failure_index = self.query_block_or_create(failure_address);
73 self.add_edge(self.current_block, failure_index, false)?;
74 let success_index = self.query_block_or_create(success_address);
75 self.add_edge(self.current_block, success_index, true)?;
76 self.current_block = success_index;
77
78 Ok(())
79 }
80 JumpType::ConditionalNotTaken => {
81 let failure_address = failure_address.ok_or(CFGError::ExpectedFailureAddress)?;
83
84 let failure_index = self.query_block_or_create(failure_address);
85 self.add_edge(self.current_block, failure_index, true)?;
86 let success_index = self.query_block_or_create(success_address);
87 self.add_edge(self.current_block, success_index, false)?;
88 self.current_block = failure_index;
89
90 Ok(())
91 }
92 }
93 }
94 }
95 }
96
97}
98
99
100pub struct BasicBlock {
101 start: usize,
103 end: usize,
105 block: HashMap<usize, BlockType>,
107 edges: Vec<(usize, usize)>
109}
110
111impl BasicBlock {
112 fn new(start:usize) -> Self {
114 BasicBlock { start: start, end: start, block: HashMap::new(), edges: Vec::new() }
115 }
116
117 fn add_instruction(&mut self, address:usize, instruction: BlockType) {
119 self.block.insert(address, instruction);
120 self.end = address;
121 }
122
123 pub fn instructions(&self) -> impl Iterator<Item=(&usize, &BlockType)> {
125 self.block.iter()
126 }
127
128 pub fn edges(&self) -> impl Iterator<Item=&(usize, usize)> {
130 self.edges.iter()
131 }
132
133 fn add_edge(&mut self, edge: usize, traversed: bool) {
135 if let Some((_, cnt)) = self.edges.iter_mut().find(|(e, _)| *e == edge) {
136 *cnt += traversed as usize;
137 } else {
138 self.edges.push((edge, traversed as usize));
139 }
140 }
141
142}
143
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn unconditional_jump() -> Result<(), CFGError> {
151 let mut cfg = ControlFlowGraph::new(2);
152 cfg.execute(3, BlockType::Instruction("INC".to_string(), None))?;
153 cfg.execute(4, BlockType::Instruction("LDAC".to_string(), Some("SomeOperand".to_string())))?;
154 cfg.execute(5, BlockType::Jump("JMP".to_string(), 9, JumpType::UnconditionalJump, None))?;
155 cfg.execute(10, BlockType::Instruction("INC".to_string(), None))?;
156 assert_eq!(2, cfg.blocks.len());
157 assert_eq!(1, cfg.blocks.get(0).unwrap().edges.len());
158
159
160 Ok(())
161 }
162
163 #[test]
164 fn conditional_jump() -> Result<(), CFGError> {
165 let mut cfg = ControlFlowGraph::new(2);
166 cfg.execute(3, BlockType::Instruction("INC".to_string(), None))?;
167 cfg.execute(4, BlockType::Instruction("LDAC".to_string(), Some("SomeOperand".to_string())))?;
168 cfg.execute(5, BlockType::Jump("JMP".to_string(), 9, JumpType::ConditionalTaken, Some(6)))?;
169 cfg.execute(10, BlockType::Instruction("INC".to_string(), None))?;
170 assert_eq!(3, cfg.blocks.len());
171 assert_eq!(1, cfg.blocks.get(0).unwrap().edges.get(1).unwrap().1);
172 assert_eq!(0, cfg.blocks.get(0).unwrap().edges.get(0).unwrap().1);
173
174 Ok(())
175 }
176
177}