diff --git a/src/Optimizations/Analysis/BlockBrs.rs b/src/Optimizations/Analysis/BlockBrs.rs new file mode 100644 index 00000000..f637cf85 --- /dev/null +++ b/src/Optimizations/Analysis/BlockBrs.rs @@ -0,0 +1,154 @@ +use std::collections::HashMap; +use crate::IR::{Block, BlockId, ir::*, Var}; + +/// analyzes which blocks can branch to which other blocks +pub struct BlockBranchAnalysis<'a> { + pub(crate) branches: HashMap<&'a String, Vec<&'a BlockId>>, +} + +impl<'a> BlockBranchAnalysis<'a> { + /// analyzes which blocks could branch to other blocks + /// + /// #### NOTE: + /// + /// It does not ignore dead code: + /// ```no-run + /// ret void 0 + /// br some_block + /// ``` + /// + /// Would still be identified as a branch to block `some_block` + pub fn analyze(blocks: &'a Vec) -> Self { + let mut branches = HashMap::new(); + + for block in blocks { + let mut brs = Vec::new(); + + for node in &block.nodes { + if let Some(br) = node.as_any().downcast_ref::>() { + brs.push(&br.inner1); + } else if let Some(br) = node.as_any().downcast_ref::>() { + brs.push(&br.inner2); + brs.push(&br.inner3); + } else if let Some(switch) = node.as_any().downcast_ref::() { + brs.push(&switch.default); + for (_, case) in &switch.cases { + brs.push(case); + } + } + } + + branches.insert(&block.name, brs); + } + + Self { + branches: branches + } + } + + /// checks if the block ... branches to block ... + /// + /// #### NOTE: + /// + /// `analyze` needs to be run first + pub fn branches_to(&self, from: &BlockId, to: &BlockId) -> bool { + let mut branches = false; + + for (br_start, br_to) in &self.branches { + if br_start == &&from.name { + if br_to.contains(&to) { + branches = true; + break; + } + } + } + + branches + } + + /// Returns if the target block branches to itself (or a block which branches to it) + /// + /// Helps with loop analysis + /// + /// #### NOTE: + /// + /// `analyze` needs to be run first + /// + /// it has a customizable depth. Which means if we give it a depth of 1 + /// that then following code will be recoginized as a branch: + /// + /// ```no-run + /// block1: + /// br block2 + /// + /// block2: + /// br block2 + /// ``` + /// + /// but: + /// ```no-run + /// block1: + /// br block2 + /// + /// block2: + /// br block3 + /// + /// block3: + /// br block4 + /// + /// block4: + /// br block1 + /// ``` + /// won't be detected + pub fn branches_to_itself(&self, target: &BlockId, depth: i32) -> bool { + // HOW IT WORKS: + + // 1. we create a list of blocks which branch to the target block + // 2. we iterate over all blocks and look if it is the target block + // then we look if the branches branch to a branch with branches + // to the target branch with a given depth + // + + let mut branches = false; + + let mut covered = Vec::new(); + + for (br_from, br_to) in &self.branches { + if br_to.contains(&target) { + covered.push(BlockId(br_from.to_owned().to_owned())); + } + } + + for _ in 0..depth { + for (br_from, br_to) in &self.branches { + if covered.contains(&BlockId(br_from.to_owned().to_owned())) { + continue; + } + + let mut coverd = false; + for cover in &covered { + if br_to.contains(&cover) { + coverd = true; + } + } + + if coverd { + covered.push(BlockId(br_from.to_owned().to_owned())); + } + } + } + + for (br_from, br_to) in &self.branches { + if br_from != &&target.name { + for br in br_to { + if covered.contains(br.to_owned()) { + branches = true; + break; + } + } + } + } + + branches + } +} \ No newline at end of file diff --git a/src/Optimizations/Analysis/mod.rs b/src/Optimizations/Analysis/mod.rs new file mode 100644 index 00000000..55af2f22 --- /dev/null +++ b/src/Optimizations/Analysis/mod.rs @@ -0,0 +1,4 @@ +#[allow(hidden_glob_reexports)] +mod BlockBrs; + +pub use BlockBrs::*; \ No newline at end of file diff --git a/src/Optimizations/mod.rs b/src/Optimizations/mod.rs index 97d661d1..5a02f59e 100644 --- a/src/Optimizations/mod.rs +++ b/src/Optimizations/mod.rs @@ -2,6 +2,8 @@ mod mngr; mod template; /// all passes pub mod Passes; +/// all analysis functions +pub mod Analysis; pub use mngr::PassManager; pub use template::Pass;