diff --git a/crates/control-flow-graph-wasm/src/lib.rs b/crates/control-flow-graph-wasm/src/lib.rs index b141a5a..d1de14e 100644 --- a/crates/control-flow-graph-wasm/src/lib.rs +++ b/crates/control-flow-graph-wasm/src/lib.rs @@ -75,7 +75,7 @@ impl DominatorRelation { self.dominates_calculate(it, visited); } } - pub fn dorminates(&self, node: u32) -> Vec { + pub fn dominates(&self, node: u32) -> Vec { let mut visited = Vec::new(); self.dominates_calculate(node, &mut visited); visited diff --git a/doc/content/tools/graph_editor.md b/doc/content/tools/graph_editor.md index 7a39c78..3478307 100644 --- a/doc/content/tools/graph_editor.md +++ b/doc/content/tools/graph_editor.md @@ -162,9 +162,9 @@ function onNodeMouseEnter(node, event) { let hoveringNodeId = parseInt(hoveringNode.getAttribute("id").slice("node-".length)); let dorminatorInfo = model.dominator_relation(); if (dorminatorInfo) { - let dorminates = dorminatorInfo.dorminates(hoveringNodeId); + let dominates = dorminatorInfo.dominates(hoveringNodeId); let dominance_frontiers = dorminatorInfo.dominance_frontiers(model, hoveringNodeId); - for (let dorminate of dorminates) { + for (let dorminate of dominates) { let idText = `node-${dorminate}`; let node = document.getElementById(idText); node.childNodes[0].setAttribute("stroke", "none"); @@ -180,9 +180,9 @@ function onNodeMouseEnter(node, event) { let hoveringNodeId = parseInt(hoveringNode.getAttribute("id").slice("node-".length)); let dorminatorInfo = model.dominator_relation(); if (dorminatorInfo) { - let dorminates = dorminatorInfo.dorminates(hoveringNodeId); + let dominates = dorminatorInfo.dominates(hoveringNodeId); let dominance_frontiers = dorminatorInfo.dominance_frontiers(model, hoveringNodeId); - for (let dorminate of dorminates) { + for (let dorminate of dominates) { let idText = `node-${dorminate}`; let node = document.getElementById(idText); node.childNodes[0].setAttribute("stroke", "#00FF33"); @@ -199,9 +199,9 @@ function onNodeMouseLeave(node, event) { let hoveringNodeId = parseInt(hoveringNode.getAttribute("id").slice("node-".length)); let dorminatorInfo = model.dominator_relation(); if (dorminatorInfo) { - let dorminates = dorminatorInfo.dorminates(hoveringNodeId); + let dominates = dorminatorInfo.dominates(hoveringNodeId); let dominance_frontiers = dorminatorInfo.dominance_frontiers(model, hoveringNodeId); - for (let dorminate of dorminates) { + for (let dorminate of dominates) { let idText = `node-${dorminate}`; let node = document.getElementById(idText); node.childNodes[0].setAttribute("stroke", "none"); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 271800c..f28e14f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly" \ No newline at end of file +channel = "nightly-2023-06-15" \ No newline at end of file diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 17a3ca8..3dd71ff 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,2 +1,4 @@ /// RISC-V backend. pub mod riscv; + +pub mod wasm; diff --git a/src/backend/wasm/_mod.rs b/src/backend/wasm/_mod.rs new file mode 100644 index 0000000..f5b04a1 --- /dev/null +++ b/src/backend/wasm/_mod.rs @@ -0,0 +1,410 @@ +use std::fmt::{self, Debug, Display}; + +use itertools::Itertools; +use petgraph::{dot::Dot, Direction}; + +use crate::{ + ir::{ + self, + analyzer::{self, Analyzer, BindedAnalyzer, IsAnalyzer, LoopContent}, + statement::IRStatement, + }, + utility::graph, +}; +#[derive(PartialEq, Clone)] +pub struct ControlFlow { + content: Vec, +} + +impl Debug for ControlFlow { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + self.content + .iter() + .map(|item| write!(f, "{:?}", item)) + .collect() + } +} + +impl ControlFlow { + pub fn node_count(&self) -> usize { + self.content.iter().map(|it| it.node_count()).sum() + } + + pub fn contains(&self, node: usize) -> bool { + for content_item in &self.content { + match content_item { + ControlFlowContent::Block(x) => { + if x.contains(node) { + return true; + } + } + ControlFlowContent::BrIf(x) => { + if *x == node { + return true; + } + } + ControlFlowContent::If(x, y) => { + if x.contains(node) { + return true; + } + if let Some(y) = y { + if y.contains(node) { + return true; + } + } + } + ControlFlowContent::Loop(x) => { + if x.contains(node) { + return true; + } + } + ControlFlowContent::Node(x) => { + if *x == node { + return true; + } + } + } + } + false + } + + pub fn first_node(&self) -> usize { + self.content.first().unwrap().first_node() + } +} + +#[derive(PartialEq, Clone)] +pub enum ControlFlowContent { + Block(Box), + BrIf(usize), + If(Box, Option), + Loop(Box), + Node(usize), +} + +impl Debug for ControlFlowContent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + ControlFlowContent::Block(cf) => { + write!(f, "Block {{ {:?}}} ", cf)?; + } + ControlFlowContent::BrIf(i) => { + write!(f, "BrIf {:?}", i)?; + } + ControlFlowContent::If(cf1, cf2) => { + write!(f, "If {{ {:?}}} ", cf1)?; + if let Some(cf2) = cf2 { + write!(f, "Else {{ {:?}}} ", cf2)?; + } + } + ControlFlowContent::Loop(cf) => { + write!(f, "Loop {{ {:?}}} ", cf)?; + } + ControlFlowContent::Node(i) => { + write!(f, "{:?} ", i)?; + } + } + Ok(()) + } +} + +impl ControlFlowContent { + pub fn node_count(&self) -> usize { + match self { + ControlFlowContent::Block(x) => x.node_count(), + ControlFlowContent::BrIf(_) => 0, + ControlFlowContent::If(x, y) => { + x.node_count() + y.as_ref().map(|it| it.node_count()).unwrap_or(0) + } + ControlFlowContent::Loop(x) => x.node_count(), + ControlFlowContent::Node(_) => 1, + } + } + + pub fn contains(&self, node: usize) -> bool { + match self { + ControlFlowContent::Block(x) => x.contains(node), + ControlFlowContent::BrIf(x) => *x == node, + ControlFlowContent::If(x, y) => { + x.contains(node) || y.as_ref().map(|it| it.contains(node)).unwrap_or(false) + } + ControlFlowContent::Loop(x) => x.contains(node), + ControlFlowContent::Node(x) => *x == node, + } + } + + pub fn first_node(&self) -> usize { + match self { + ControlFlowContent::Block(x) => x.first_node(), + ControlFlowContent::BrIf(_) => panic!(), + ControlFlowContent::If(x, _) => x.first_node(), + ControlFlowContent::Loop(x) => x.first_node(), + ControlFlowContent::Node(x) => *x, + } + } +} + +fn fold_loop(current: &mut Vec, loop_item: &analyzer::Loop) { + let entry = loop_item.entries.first().unwrap(); + let slice_start = current + .iter() + .position(|it| { + if let ControlFlowContent::Node(x) = it { + x == entry + } else { + false + } + }) + .unwrap(); + let slice_end = slice_start + loop_item.node_count(); + let mut removed = current.drain(slice_start..slice_end).collect_vec(); + let sub_loops = loop_item.content.iter().filter_map(|it| { + if let LoopContent::SubLoop(subloop) = it { + Some(subloop) + } else { + None + } + }); + for sub_loop in sub_loops { + fold_loop(&mut removed, &sub_loop); + } + let new_loop_item = ControlFlowContent::Loop(Box::new(ControlFlow { content: removed })); + current.insert(slice_start, new_loop_item); +} + +fn nest_branch(current: &mut Vec, analyzer: &BindedAnalyzer) { + let graph = analyzer.control_flow_graph(); + let mut current_index = 0; + while current_index < current.len() { + let current_item = ¤t[current_index]; + match current_item { + ControlFlowContent::Node(node) => { + let dominates = graph.dominates(*node); + let predecessors = graph + .graph() + .neighbors_directed((*node).into(), Direction::Incoming) + .filter(|it| !dominates.contains(&it.index())) + .collect_vec(); + if predecessors.len() == 1 { + // try nesting `node` and nodes dominated by `node` + // into branch started by `predecessor` + let predecessor = predecessors[0]; + println!("nest {} into {:?}", node, predecessor); + let predecessor_last = + analyzer.bind_on.content[predecessor.index()].content.last(); + if let Some(IRStatement::Branch(branch)) = predecessor_last { + println!("{:?} is already a branch", predecessor); + let predecessor_index = current + .iter() + .position(|it| it.contains(predecessor.index())) + .unwrap(); + let nodes_dominated_by_current_node = graph + .dominates(*node) + .into_iter() + .filter(|it| it != node) + .collect_vec(); + dbg!(&nodes_dominated_by_current_node); + let initial_index = current_index; + current_index += 1; + while nodes_dominated_by_current_node + .iter() + .find(|it| current[current_index].contains(**it)) + .is_some() + { + current_index += 1; + } + let mut content = current.drain(initial_index..current_index).collect(); + nest_branch(&mut content, analyzer); + let after_predecessor = current.get_mut(predecessor_index + 1); + // FIXME: is it in order? + if let Some(ControlFlowContent::If(_, else_part)) = after_predecessor { + *else_part = Some(ControlFlow { content }) + } else { + current.insert( + initial_index, + ControlFlowContent::If(Box::new(ControlFlow { content }), None), + ); + } + } else { + current_index += 1; + } + } else { + current_index += 1; + } + } + ControlFlowContent::Loop(loop_item) => { + let first = loop_item.first_node(); + let dominates = graph.dominates(first); + let predecessors = graph + .graph() + .neighbors_directed(first.into(), Direction::Incoming) + .filter(|it| !dominates.contains(&it.index())) + .collect_vec(); + if predecessors.len() == 1 { + // try nesting `loop_item` and nodes dominated by `loop_item` + // into branch started by `predecessor` + let predecessor = predecessors[0]; + println!("nest {:?} into {:?}", loop_item, predecessor); + let predecessor_last_statement = + analyzer.bind_on.content[predecessor.index()].content.last(); + if let Some(IRStatement::Branch(branch)) = predecessor_last_statement { + let predecessor_index = current + .iter() + .position(|it| it.contains(predecessor.index())) + .unwrap(); + let mut content = vec![current.remove(current_index)]; + nest_branch(&mut content, analyzer); + let after_predecessor = current.get_mut(predecessor_index + 1); + if let Some(ControlFlowContent::If(_, else_part)) = after_predecessor { + *else_part = Some(ControlFlow { content }); + } else { + current.insert( + current_index, + ControlFlowContent::If(Box::new(ControlFlow { content }), None), + ); + } + } else { + current_index += 1; + } + } else { + current_index += 1; + } + } + ControlFlowContent::Block(_) + | ControlFlowContent::BrIf(_) + | ControlFlowContent::If(_, _) => current_index += 1, + } + dbg!(¤t); + } +} + +// function must be reduciable and in correct topo order +fn generate_control_flow( + function: &ir::FunctionDefinition, + analyzer: &Analyzer, +) -> Vec { + let binded_analyzer = analyzer.bind(function); + let mut current = function + .content + .iter() + .enumerate() + .map(|(index, _)| ControlFlowContent::Node(index)) + .collect_vec(); + fold_all_loops(binded_analyzer, &mut current); + let binded_analyzer = analyzer.bind(function); + nest_branch(&mut current, &binded_analyzer); + current +} + +fn fold_all_loops( + binded_analyzer: analyzer::BindedAnalyzer, + initial: &mut Vec, +) { + let analyzed_loops = binded_analyzer.control_flow_graph().loops(); + let sub_loops = analyzed_loops.content.iter().filter_map(|it| { + if let LoopContent::SubLoop(subloop) = it { + Some(subloop) + } else { + None + } + }); + for sub_loop in sub_loops { + fold_loop(initial, &sub_loop); + } +} + +#[test] +fn test_fold() { + use crate::{ + ir::{ + function::{basic_block::BasicBlock, test_util::*}, + statement::Ret, + FunctionDefinition, + }, + utility::data_type, + }; + let function_definition = FunctionDefinition { + header: ir::FunctionHeader { + name: "f".to_string(), + parameters: Vec::new(), + return_type: data_type::Type::None, + }, + content: vec![ + BasicBlock { + name: Some("bb0".to_string()), + content: vec![branch("bb1", "bb7")], + }, + BasicBlock { + name: Some("bb1".to_string()), + content: vec![jump("bb2")], + }, + BasicBlock { + name: Some("bb2".to_string()), + content: vec![jump("bb3")], + }, + BasicBlock { + name: Some("bb3".to_string()), + content: vec![jump("bb4")], + }, + BasicBlock { + name: Some("bb4".to_string()), + content: vec![branch("bb5", "bb6")], + }, + BasicBlock { + name: Some("bb5".to_string()), + content: vec![jump("bb2")], + }, + BasicBlock { + name: Some("bb6".to_string()), + content: vec![branch("bb1", "bb14")], + }, + BasicBlock { + name: Some("bb7".to_string()), + content: vec![branch("bb8", "bb9")], + }, + BasicBlock { + name: Some("bb8".to_string()), + content: vec![jump("bb10")], + }, + BasicBlock { + name: Some("bb10".to_string()), + content: vec![jump("bb11")], + }, + BasicBlock { + name: Some("bb9".to_string()), + content: vec![jump("bb11")], + }, + BasicBlock { + name: Some("bb11".to_string()), + content: vec![branch("bb12", "bb13")], + }, + BasicBlock { + name: Some("bb12".to_string()), + content: vec![jump("bb13")], + }, + BasicBlock { + name: Some("bb13".to_string()), + content: vec![jump("bb14")], + }, + BasicBlock { + name: Some("bb14".to_string()), + content: vec![jump("bb15")], + }, + BasicBlock { + name: Some("bb15".to_string()), + content: vec![Ret { value: None }.into()], + }, + ], + }; + let analyzer = Analyzer::default(); + let graph = analyzer.bind(&function_definition); + let binding = graph.control_flow_graph(); + let graph = binding.graph(); + println!("{:?}", Dot::new(&graph)); + let result = generate_control_flow(&function_definition, &analyzer); + dbg!(result); +} + +// ABC +// --- +// -- \ No newline at end of file diff --git a/src/backend/wasm/mod.rs b/src/backend/wasm/mod.rs new file mode 100644 index 0000000..aa0089f --- /dev/null +++ b/src/backend/wasm/mod.rs @@ -0,0 +1,598 @@ +use itertools::Itertools; + +use crate::ir::{ + analyzer::{self, BindedControlFlowGraph, LoopContent}, + statement::IRStatement, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ControlFlowContent { + Block(Vec), + If(Vec, Vec), + Loop(Vec), + Node(usize), +} + +impl ControlFlowContent { + pub fn new_block(content: Vec) -> Self { + Self::Block(content) + } + + pub fn new_if(taken: Vec, untaken: Vec) -> Self { + Self::If(taken, untaken) + } + + pub fn new_loop(content: Vec) -> Self { + Self::Loop(content) + } + + pub fn new_node(node: usize) -> Self { + Self::Node(node) + } + + pub fn first_node(&self) -> usize { + match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + content.first().unwrap().first_node() + } + ControlFlowContent::If(taken, _untaken) => taken.first().unwrap().first_node(), + ControlFlowContent::Node(n) => *n, + } + } + + pub fn get + Clone>(&self, index: &[T]) -> Option<&Self> { + let current_index = index.get(0)?.clone(); + let current_index = current_index.into(); + let current = match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + content.get(current_index) + } + ControlFlowContent::If(taken, untaken) => taken + .get(current_index) + .or_else(|| untaken.get(current_index - taken.len())), + ControlFlowContent::Node(_) => { + if current_index == 0 { + Some(self) + } else { + return None; + } + } + }; + let rest_index = &index[1..]; + if rest_index.is_empty() { + current + } else { + current?.get(rest_index) + } + } + + pub fn get_mut + Clone>(&mut self, index: &[T]) -> Option<&mut Self> { + let current_index = index.get(0)?.clone(); + let current_index = current_index.into(); + let current = match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + content.get_mut(current_index) + } + ControlFlowContent::If(taken, untaken) => { + if current_index < taken.len() { + taken.get_mut(current_index) + } else { + untaken.get_mut(current_index - taken.len()) + } + } + ControlFlowContent::Node(_) => { + if current_index == 0 { + Some(self) + } else { + return None; + } + } + }; + let rest_index = &index[1..]; + if rest_index.is_empty() { + current + } else { + current?.get_mut(rest_index) + } + } + + pub fn contains(&self, node: usize) -> bool { + match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + content.iter().any(|it| it.contains(node)) + } + ControlFlowContent::If(taken, untaken) => taken + .iter() + .chain(untaken.iter()) + .any(|it| it.contains(node)), + ControlFlowContent::Node(n) => *n == node, + } + } + + pub fn remove + Clone>(&mut self, index: &[T]) -> Option { + if index.is_empty() { + None + } else if index.len() == 1 { + let index = index[0].clone().into(); + match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + Some(content.remove(index)) + } + ControlFlowContent::If(taken, untaken) => { + if index < taken.len() { + Some(taken.remove(index)) + } else { + Some(untaken.remove(index)) + } + } + ControlFlowContent::Node(n) => { + panic!("unable to remove the {index}th element from node {n}") + } + } + } else { + self.get_mut(&[index[0].clone()]) + .unwrap() + .remove(&index[1..]) + } + } + + pub fn position(&self, item: &Self) -> Option> { + match self { + ControlFlowContent::Block(content) | ControlFlowContent::Loop(content) => { + for (i, subblock) in content.iter().enumerate() { + let mut potential_result = vec![i]; + if subblock == item { + return Some(potential_result); + } else if let Some(result) = subblock.position(item) { + potential_result.extend_from_slice(&result); + return Some(potential_result); + } + } + } + ControlFlowContent::If(taken, untaken) => { + for (i, subblock) in taken.iter().chain(untaken.iter()).enumerate() { + let mut potential_result = vec![i]; + if subblock == item { + return Some(potential_result); + } else if let Some(result) = subblock.position(item) { + potential_result.extend_from_slice(&result); + return Some(potential_result); + } + } + } + ControlFlowContent::Node(_) => { + if self == item { + return Some(vec![0]); + } + } + } + None + } + + pub fn nodes(&self) -> ControlFlowNodesIter { + ControlFlowNodesIter::new(self) + } +} + +pub struct ControlFlowNodesIter<'a> { + bind_on: &'a ControlFlowContent, + pub current_index: Vec, +} + +impl<'a> ControlFlowNodesIter<'a> { + pub fn new(bind_on: &'a ControlFlowContent) -> Self { + Self { + bind_on, + current_index: vec![0], + } + } + + pub fn from_index(bind_on: &'a ControlFlowContent, index: &[usize]) -> Self { + Self { + bind_on, + current_index: index.to_vec(), + } + } +} + +impl Iterator for ControlFlowNodesIter<'_> { + type Item = (Vec, usize); + + fn next(&mut self) -> Option { + match self.bind_on.get(&self.current_index) { + Some(ControlFlowContent::Block(_)) + | Some(ControlFlowContent::Loop(_)) + | Some(ControlFlowContent::If(_, _)) => { + self.current_index.push(0); + self.next() + } + Some(ControlFlowContent::Node(n)) => { + let result = self.current_index.clone(); + *self.current_index.last_mut().unwrap() += 1; + Some((result, *n)) + } + None => { + if self.current_index.len() != 1 { + self.current_index.pop(); + *self.current_index.last_mut().unwrap() += 1; + self.next() + } else { + None + } + } + } + } +} + +fn fold_loop(current: &mut ControlFlowContent, loop_item: &analyzer::Loop) { + let (to_remove_indexes, to_remove_items): (Vec<_>, Vec<_>) = current + .nodes() + .filter(|(_, n)| loop_item.is_node_in((*n).into())) + .unzip(); + for to_remove_index in to_remove_indexes[1..].iter().rev() { + current.remove(to_remove_index); + } + let first = current.get_mut(&to_remove_indexes[0]).unwrap(); + let new_loop_item = ControlFlowContent::Loop( + to_remove_items + .into_iter() + .map(ControlFlowContent::Node) + .collect(), + ); + *first = new_loop_item; + for content in &loop_item.content { + if let LoopContent::SubLoop(subloop) = content { + fold_loop(first, subloop); + } + } +} + +fn fold_if_else_once(current: &mut ControlFlowContent, graph: &BindedControlFlowGraph) -> bool { + // A node is foldable if its only successor is an branch block + // So for each branch block: + // - if the "next" block has only one successor, nest it and nodes dominated by it in an if + // - if (the "next" block after the new nested if)'s only successor is also the block, nest it in the else part + let (node_indexes, nodes): (Vec<_>, Vec<_>) = current.nodes().unzip(); + let mut considering_node_index = 0; + let mut folded = false; + while considering_node_index < nodes.len() - 1 { + let node = nodes[considering_node_index]; + let mut next_node_index = node_indexes[considering_node_index].clone(); + *next_node_index.last_mut().unwrap() += 1; + if matches!( + current.get(&next_node_index), + Some(ControlFlowContent::If(_, _)) + ) { + // already nested, just consider next + considering_node_index += 1; + continue; + } + let block = &graph.bind_on.content[node]; + let last_statement = block.content.last().unwrap(); + if let IRStatement::Branch(_) = last_statement { + let next_node = nodes[considering_node_index + 1]; + if graph.not_dominate_successors(next_node).len() == 1 { + let nodes_dominated_by_next_node = graph.dominates(next_node); + let mut to_nest = Vec::new(); + let mut next_to_nest_index = node_indexes[considering_node_index + 1].clone(); + // the next node is deep nested in loops, so we need to fold all structure the node is in + while next_to_nest_index.len() > node_indexes[considering_node_index].len() { + next_to_nest_index.pop(); + } + while let Some(next_to_nest) = current.get(&next_to_nest_index) && nodes_dominated_by_next_node.contains(&next_to_nest.first_node()) { + to_nest.push(next_to_nest_index.clone()); + *next_to_nest_index.last_mut().unwrap() += 1; + } + let initial_considering_node_index = considering_node_index; + considering_node_index += to_nest + .iter() + .map(|it| current.get(it).unwrap().nodes().count()) + .sum::(); + let (to_replace, to_remove) = to_nest.split_first().unwrap(); + let removed = to_remove + .iter() + .map(|it| current.remove(it).unwrap()) + .collect_vec(); + // nest else part + let node_after_nest = nodes[considering_node_index]; + let node_after_nest_successors = graph.not_dominate_successors(node_after_nest); + let untaken_content = if node_after_nest_successors.len() == 1 { + let nodes_dominated_by_node_after_nest = graph.dominates(node_after_nest); + let mut to_nest = Vec::new(); + let mut next_to_nest_index = node_indexes[considering_node_index].clone(); + // the next node is deep nested in loops, so we need to fold all structure the node is in + while next_to_nest_index.len() + > node_indexes[initial_considering_node_index].len() + { + next_to_nest_index.pop(); + } + while let Some(next_to_nest) = current.get(&next_to_nest_index) && nodes_dominated_by_node_after_nest.contains(&next_to_nest.first_node()) { + to_nest.push(next_to_nest_index.clone()); + *next_to_nest_index.last_mut().unwrap() += 1; + } + considering_node_index += to_nest.len(); + to_nest + .iter() + .map(|it| current.remove(it).unwrap()) + .collect_vec() + } else { + Vec::new() + }; + let replaced_node = current.get_mut(to_replace).unwrap(); + let mut taken_content = vec![replaced_node.clone()]; + taken_content.extend_from_slice(&removed); + let new_if_node = ControlFlowContent::If(taken_content, untaken_content); + *replaced_node = new_if_node; + folded = true; + } else { + considering_node_index += 1; + } + } else { + considering_node_index += 1; + } + } + folded +} + +fn fold_if_else(current: &mut ControlFlowContent, graph: &BindedControlFlowGraph) { + let mut folded = true; + while folded { + folded = fold_if_else_once(current, graph); + } +} +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + ir::{ + self, + analyzer::{ControlFlowGraph, IsAnalyzer}, + function::{basic_block::BasicBlock, test_util::*}, + statement::Ret, + FunctionDefinition, + }, + utility::data_type, + }; + #[test] + fn control_flow_content_get() { + let content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_if( + vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ], + vec![ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ])], + ), + ]); + assert_eq!( + content.get(&[0usize]), + Some(&ControlFlowContent::new_node(0)) + ); + assert_eq!( + content.get(&[1usize, 0]), + Some(&ControlFlowContent::new_node(1)) + ); + assert_eq!( + content.get(&[1usize, 2]), + Some(&ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ])) + ); + assert_eq!( + content.get(&[1usize, 2, 0]), + Some(&ControlFlowContent::new_node(3)) + ); + assert_eq!(content.get(&[2usize, 0, 2]), None); + assert_eq!(content.get(&[3usize]), None); + } + + #[test] + fn control_flow_content_position() { + let content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_if( + vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ], + vec![ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ])], + ), + ]); + assert_eq!( + content.position(&ControlFlowContent::new_node(0)), + Some(vec![0]) + ); + assert_eq!( + content.position(&ControlFlowContent::new_node(1)), + Some(vec![1, 0]) + ); + assert_eq!( + content.position(&ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ])), + Some(vec![1, 2]) + ); + assert_eq!( + content.position(&ControlFlowContent::new_node(3)), + Some(vec![1, 2, 0]) + ); + assert_eq!(content.position(&ControlFlowContent::new_node(5)), None); + } + + #[test] + fn control_flow_nodes() { + let content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_if( + vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ], + vec![ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ])], + ), + ControlFlowContent::new_node(5), + ]); + let mut iter = content.nodes(); + assert_eq!(iter.next(), Some((vec![0], 0))); + assert_eq!(iter.next(), Some((vec![1, 0], 1))); + assert_eq!(iter.next(), Some((vec![1, 1], 2))); + assert_eq!(iter.next(), Some((vec![1, 2, 0], 3))); + assert_eq!(iter.next(), Some((vec![1, 2, 1], 4))); + assert_eq!(iter.next(), Some((vec![2], 5))); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_fold_loop() { + let mut content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ]); + let loop_item = analyzer::Loop { + entries: vec![1], + content: vec![ + analyzer::LoopContent::Node(1), + analyzer::LoopContent::Node(2), + analyzer::LoopContent::Node(3), + ], + }; + fold_loop(&mut content, &loop_item); + assert_eq!( + content, + ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ControlFlowContent::new_node(3), + ]), + ControlFlowContent::new_node(4), + ]) + ); + + let mut content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ControlFlowContent::new_node(5), + ControlFlowContent::new_node(6), + ]); + let loop_item = analyzer::Loop { + entries: vec![1], + content: vec![ + analyzer::LoopContent::Node(1), + analyzer::LoopContent::Node(2), + analyzer::LoopContent::SubLoop(Box::new(analyzer::Loop { + entries: vec![3], + content: vec![ + analyzer::LoopContent::Node(3), + analyzer::LoopContent::Node(4), + analyzer::LoopContent::Node(5), + ], + })), + ], + }; + fold_loop(&mut content, &loop_item); + assert_eq!( + content, + ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ControlFlowContent::new_node(5), + ]), + ]), + ControlFlowContent::new_node(6), + ]) + ); + } + + #[test] + fn test_fold_if_else() { + let function_definition = FunctionDefinition { + header: ir::FunctionHeader { + name: "f".to_string(), + parameters: Vec::new(), + return_type: data_type::Type::None, + }, + content: vec![ + BasicBlock { + name: Some("bb0".to_string()), + content: vec![branch("bb1", "bb2")], + }, + BasicBlock { + name: Some("bb1".to_string()), + content: vec![jump("bb3")], + }, + BasicBlock { + name: Some("bb3".to_string()), + content: vec![branch("bb4", "bb5")], + }, + BasicBlock { + name: Some("bb4".to_string()), + content: vec![jump("bb6")], + }, + BasicBlock { + name: Some("bb5".to_string()), + content: vec![jump("bb6")], + }, + BasicBlock { + name: Some("bb6".to_string()), + content: vec![branch("bb1", "bb7")], + }, + BasicBlock { + name: Some("bb2".to_string()), + content: vec![jump("bb7")], + }, + BasicBlock { + name: Some("bb7".to_string()), + content: vec![Ret { value: None }.into()], + }, + ], + }; + let control_flow_graph = ControlFlowGraph::new(); + let binded = control_flow_graph.bind(&function_definition); + let mut content = ControlFlowContent::new_block(vec![ + ControlFlowContent::new_node(0), + ControlFlowContent::new_loop(vec![ + ControlFlowContent::new_node(1), + ControlFlowContent::new_node(2), + ControlFlowContent::new_node(3), + ControlFlowContent::new_node(4), + ControlFlowContent::new_node(5), + ]), + ControlFlowContent::new_node(6), + ControlFlowContent::new_node(7), + ]); + fold_if_else(&mut content, &binded); + assert!(matches!( + content.get(&[1usize]), + Some(ControlFlowContent::If(_, _)) + )); + assert!(matches!( + content.get(&[1usize, 0, 2]), + Some(ControlFlowContent::If(_, _)) + )); + } +} diff --git a/src/ir/editor/analyzer/control_flow/mod.rs b/src/ir/editor/analyzer/control_flow/mod.rs index 7d40afe..0ca3663 100644 --- a/src/ir/editor/analyzer/control_flow/mod.rs +++ b/src/ir/editor/analyzer/control_flow/mod.rs @@ -181,7 +181,7 @@ impl ControlFlowGraph { } pub struct BindedControlFlowGraph<'item, 'bind: 'item> { - bind_on: &'bind FunctionDefinition, + pub bind_on: &'bind FunctionDefinition, item: &'item ControlFlowGraph, } @@ -207,6 +207,30 @@ impl<'item, 'bind: 'item> BindedControlFlowGraph<'item, 'bind> { pub fn dominates(&self, bb_index: usize) -> Vec { self.item.dominate(self.bind_on, bb_index) } + pub fn predecessor(&self, bb_index: usize) -> Vec { + self.graph() + .neighbors_directed(bb_index.into(), Direction::Incoming) + .map(|it| it.index()) + .collect() + } + + pub fn successors(&self, bb_index: usize) -> Vec { + self.graph() + .neighbors_directed(bb_index.into(), Direction::Incoming) + .map(|it| it.index()) + .collect() + } + + pub fn not_dominate_successors(&self, bb_index: usize) -> Vec { + let successors = self + .graph() + .neighbors_directed(bb_index.into(), Direction::Incoming) + .map(|it| it.index()); + let nodes_dominated = self.dominates(bb_index); + successors + .filter(|it| !nodes_dominated.contains(it)) + .collect() + } } impl<'item, 'bind: 'item> IsAnalyzer<'item, 'bind> for ControlFlowGraph { diff --git a/src/ir/editor/analyzer/mod.rs b/src/ir/editor/analyzer/mod.rs index b1f425d..9eb3749 100644 --- a/src/ir/editor/analyzer/mod.rs +++ b/src/ir/editor/analyzer/mod.rs @@ -2,7 +2,7 @@ use crate::ir::{self, FunctionDefinition}; use self::register_usage::RegisterUsageAnalyzer; pub use self::{ - control_flow::{BindedControlFlowGraph, ControlFlowGraph, Loop}, + control_flow::{BindedControlFlowGraph, ControlFlowGraph, Loop, LoopContent}, memory_usage::{BindedMemoryUsage, MemoryUsage}, register_usage::{BindedRegisterUsage, BindedRegisterUsageAnalyzer}, }; @@ -36,7 +36,7 @@ impl Analyzer { } } pub struct BindedAnalyzer<'item, 'bind: 'item> { - bind_on: &'bind FunctionDefinition, + pub bind_on: &'bind FunctionDefinition, item: &'item Analyzer, } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index efcfbfd..f30f5c2 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -29,7 +29,7 @@ pub use quantity::RegisterName; pub use type_definition::TypeDefinition; use type_definition::TypeDefinitionMapping; -mod editor; +pub mod editor; use self::function::parameter::Parameter; pub use editor::analyzer; diff --git a/src/ir/optimize/pass/fix_irreducible.rs b/src/ir/optimize/pass/fix_irreducible.rs index 4aa3281..f3ae4d8 100644 --- a/src/ir/optimize/pass/fix_irreducible.rs +++ b/src/ir/optimize/pass/fix_irreducible.rs @@ -48,7 +48,9 @@ fn fold_entries_once( let mut last_bb_last_statement = editor.content[last.index()].content.last().unwrap().clone(); editor.remove_statement((last.index(), last_bb_size - 1)); - let IRStatement::Branch(branch) = &mut last_bb_last_statement else { unreachable!() }; + let IRStatement::Branch(branch) = &mut last_bb_last_statement else { + unreachable!() + }; branch.failure_label = new_node_name.clone(); editor.push_back_statement(last.index(), last_bb_last_statement); } @@ -79,7 +81,9 @@ fn fold_entries_once( let mut last_bb_last_statement = editor.content[last.index()].content.last().unwrap().clone(); editor.remove_statement((last.index(), last_bb_size - 1)); - let IRStatement::Branch(branch) = &mut last_bb_last_statement else { unreachable!() }; + let IRStatement::Branch(branch) = &mut last_bb_last_statement else { + unreachable!() + }; let (branch_to, blocks_into_branch_to) = entries.pop().unwrap(); let branch_to_bb_name = editor.content[branch_to.index()].name.clone().unwrap(); branch.failure_label = branch_to_bb_name; diff --git a/src/ir/optimize/pass/mod.rs b/src/ir/optimize/pass/mod.rs index 4ae2864..ba7b319 100644 --- a/src/ir/optimize/pass/mod.rs +++ b/src/ir/optimize/pass/mod.rs @@ -6,12 +6,13 @@ mod remove_unused_register; mod topological_sort; use crate::ir::editor::Editor; use enum_dispatch::enum_dispatch; -use fix_irreducible::FixIrreducible; +pub use fix_irreducible::FixIrreducible; use memory_to_register::MemoryToRegister; use remove_load_directly_after_store::RemoveLoadDirectlyAfterStore; use remove_only_once_store::RemoveOnlyOnceStore; use remove_unused_register::RemoveUnusedRegister; use std::str::FromStr; +pub use topological_sort::TopologicalSort; /// This trait should be implemented by all passes which can do optimizing on ir function. #[enum_dispatch] pub trait IsPass { @@ -33,6 +34,7 @@ pub enum Pass { RemoveLoadDirectlyAfterStore, MemoryToRegister, FixIrreducible, + TopologicalSort, } impl FromStr for Pass { @@ -46,6 +48,8 @@ impl FromStr for Pass { RemoveLoadDirectlyAfterStore, )), "MemoryToRegister" => Ok(Self::MemoryToRegister(MemoryToRegister)), + "FixIrreducible" => Ok(Self::FixIrreducible(FixIrreducible)), + "TopologicalSort" => Ok(Self::TopologicalSort(TopologicalSort)), _ => Err(()), } } diff --git a/src/ir/optimize/pass/topological_sort.rs b/src/ir/optimize/pass/topological_sort.rs index d290c16..2f9a859 100644 --- a/src/ir/optimize/pass/topological_sort.rs +++ b/src/ir/optimize/pass/topological_sort.rs @@ -10,6 +10,7 @@ use crate::ir::{ use super::IsPass; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct TopologicalSort; impl IsPass for TopologicalSort { @@ -87,6 +88,7 @@ pub fn topological_order(graph: &BindedControlFlowGraph, top_level: &Loop) -> Ve topological_order_dfs(graph, top_level, 0.into(), &mut visited, &mut order); order.reverse(); let mut order: Vec = order.into_iter().map(NodeIndex::index).collect(); + dbg!(&order); let exit_block_position = order.iter().position_max().unwrap(); order.remove(exit_block_position); order diff --git a/src/lib.rs b/src/lib.rs index ec291d7..d602aa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![feature(lazy_cell)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(hash_drain_filter)] #![feature(exact_size_is_empty)]