ctrl_flow/
lib.rs

1use std::collections::HashMap;
2use crate::types::*;
3pub mod types;
4
5pub struct ControlFlowGraph {
6    /// The indice of the current block
7    current_block: usize,
8    /// The BasicBlocks found inside this given ControlFlowGraph
9    blocks: Vec<BasicBlock>
10}
11
12impl ControlFlowGraph {
13    /// Generates a ControlFlowGraph, starting at the given entry point address.
14    fn new(entry_point: usize) -> Self {
15        ControlFlowGraph { current_block: 0, blocks: vec![BasicBlock::new(entry_point)] }
16    }
17
18    /// Adds an edge to a BasicBlock, connecting src_block to dest_block.
19    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    /// Adds a BasicBlock to the ControlFlowGraph and returns the position of the BasicBlock.
26    fn add_block(&mut self, block: BasicBlock) -> usize {
27        self.blocks.push(block);
28        self.blocks.len() - 1
29    }
30
31    /// Searches for the block with the given start address and returns the position of it or creates a new one.
32    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    /// Returns an iterator over the BasicBlocks inside the ControlFlowGraph
37    pub fn blocks(&self) -> impl Iterator<Item=&BasicBlock> {
38        self.blocks.iter()
39    }
40
41    /// Executes the given BlockType on the ControlFlowGraph
42    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."); // TODO: Potentially generate an error-type?
47                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                // Add the instruction to the current block, if we already haven't
55                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                    // NOTE: Should check if this creating a copy or just using move semantics to use the same thing of memory...
59                    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                        // Failure address needs to be defined.
70                        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                        // Failure address needs to be defined.
82                        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    /// The starting address of this basic block.
102    start: usize,
103    /// The current end address of this basic block.
104    end: usize,
105    /// The mapping of each address to its respective BlockType.
106    block: HashMap<usize, BlockType>,
107    /// The edges for the given basic block which are indices to other BasicBlocks
108    edges: Vec<(usize, usize)>
109}
110
111impl BasicBlock {
112    /// Generates a new BasicBlock with a given start address
113    fn new(start:usize) -> Self {
114        BasicBlock { start: start, end: start, block: HashMap::new(), edges: Vec::new() }
115    }
116
117    /// Adds an instruction of BlockType to the given BasicBlock at the given address in the underlying HashMap.
118    fn add_instruction(&mut self, address:usize, instruction: BlockType) {
119        self.block.insert(address, instruction);
120        self.end = address;
121    }
122
123    /// Returns an iterator of the address/instruction pairs inside the underlying HashMap.
124    pub fn instructions(&self) -> impl Iterator<Item=(&usize, &BlockType)> {
125        self.block.iter()
126    }
127
128    /// Returns an iterator of the edges/count pairs inside the underlying Vector.
129    pub fn edges(&self) -> impl Iterator<Item=&(usize, usize)> {
130        self.edges.iter()
131    }
132
133    /// Adds a new edge if it cannot find it, otherwise increments the edge counter depending on if it was traversed or not.
134    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}