From 8ba6b7816630bc7ccd837500e182c71453e26e07 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Sat, 11 May 2024 00:47:00 +0200 Subject: [PATCH 1/3] upgrade rust toolchain --- integration-test/integration_test.py | 1 + rust-toolchain.toml | 2 +- src/backend/riscv/mod.rs | 2 +- src/lib.rs | 3 +-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-test/integration_test.py b/integration-test/integration_test.py index eb52e64..ca6fae7 100755 --- a/integration-test/integration_test.py +++ b/integration-test/integration_test.py @@ -33,6 +33,7 @@ def color_diff(diff): command = ["./target/debug/come", "-i", "{}{}/{}.come".format(CASE_DIR, casename, casename), "-o", "{}{}/{}.asm".format(CASE_DIR, casename, casename), + "-t", "riscv", "--emit-ir", "{}{}/{}.cmir".format(CASE_DIR, casename, casename)] if path.exists("{}{}/road.json".format(CASE_DIR, casename, casename)): road = json.load( diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f28e14f..c5b3b9f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly-2023-06-15" \ No newline at end of file +channel = "nightly-2024-05-02" diff --git a/src/backend/riscv/mod.rs b/src/backend/riscv/mod.rs index b3bc08e..8b3b98e 100644 --- a/src/backend/riscv/mod.rs +++ b/src/backend/riscv/mod.rs @@ -220,7 +220,7 @@ fn parse_single_section( .push(index); } } - pending_symbols.drain_filter(|name, indexes| { + pending_symbols.extract_if(|name, indexes| { if let Some(symbol_offset_bytes) = all_symbols.get(name) { for index in indexes { simple_instructions[*index].decide_symbol(&Symbol { diff --git a/src/lib.rs b/src/lib.rs index f1eb088..9f4b6f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,8 @@ #![feature(lazy_cell)] #![feature(if_let_guard)] #![feature(let_chains)] -#![feature(hash_drain_filter)] +#![feature(hash_extract_if)] #![feature(exact_size_is_empty)] -// #![feature(arbitrary_self_types)] #![feature(assert_matches)] /// Definitions of AST nodes and their parser. pub mod ast; From 0ff30196a14348c665795c6a2b4b89d82fe0fd0b Mon Sep 17 00:00:00 2001 From: longfangsong Date: Thu, 20 Jun 2024 18:02:01 +0200 Subject: [PATCH 2/3] structual analysis --- Cargo.toml | 8 +- src/ir/editor/analyzer/control_flow/mod.rs | 18 +- .../analyzer/control_flow/structural/mod.rs | 745 ++++++++++++++++++ .../control_flow/structural/selector.rs | 234 ++++++ src/ir/function/mod.rs | 14 +- src/ir/function/statement/branch.rs | 8 +- src/ir/function/statement/calculate/binary.rs | 8 +- src/ir/function/statement/calculate/unary.rs | 4 +- src/ir/function/statement/call.rs | 4 +- src/ir/optimize/pass/topological_sort.rs | 4 +- 10 files changed, 1032 insertions(+), 15 deletions(-) create mode 100644 src/ir/editor/analyzer/control_flow/structural/mod.rs create mode 100644 src/ir/editor/analyzer/control_flow/structural/selector.rs diff --git a/Cargo.toml b/Cargo.toml index 1a04c50..b50a161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,23 +11,23 @@ bitvec = { version = "1.0.1", features = ["serde"] } clap = { version = "4.4.14", features = ["derive"] } enum_dispatch = "0.3.12" indexmap = "2.1.0" -itertools = "0.12.0" +itertools = "0.13.0" nom = "7.1.3" paste = "1.0.14" petgraph = "0.6.4" phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0.195", features = ["derive"] } toml = "0.8.8" -shadow-rs = { version = "0.26.0", optional = true } +shadow-rs = { version = "0.28.0", optional = true } ezio = { version = "0.1.2", optional = true } delegate = "0.12.0" -wasm-encoder = "0.205.0" +wasm-encoder = "0.209.1" [dev-dependencies] cov-mark = "1.1.0" [build-dependencies] -shadow-rs = "0.26.0" +shadow-rs = "0.28.0" [lib] crate-type = ["lib"] diff --git a/src/ir/editor/analyzer/control_flow/mod.rs b/src/ir/editor/analyzer/control_flow/mod.rs index c99fe9b..acbf79a 100644 --- a/src/ir/editor/analyzer/control_flow/mod.rs +++ b/src/ir/editor/analyzer/control_flow/mod.rs @@ -11,6 +11,7 @@ use petgraph::{ dominators::{simple_fast, Dominators}, }, prelude::*, + visit::{depth_first_search, DfsEvent}, }; use crate::{ @@ -23,6 +24,7 @@ pub use self::scc::BindedScc; use super::IsAnalyzer; mod scc; +mod structural; /// [`ControlFlowGraph`] is the control flow graph and related infomation of a function. #[derive(Debug)] @@ -87,7 +89,7 @@ impl ControlFlowGraphContent { } } - /// [Dorminance Frontier](https://en.wikipedia.org/wiki/Dominator_(graph_theory)) of basic block indexed by `bb_index`. + /// [Dominance Frontier](https://en.wikipedia.org/wiki/Dominator_(graph_theory)) of basic block indexed by `bb_index`. pub fn dominance_frontier(&self, bb_index: usize) -> &[usize] { self.frontiers.get(&bb_index).unwrap() } @@ -176,7 +178,6 @@ impl ControlFlowGraph { fn dominate(&self, content: &ir::FunctionDefinition, bb_index: usize) -> Vec { self.content(content).dominates(bb_index) } - fn branch_direction( &self, content: &FunctionDefinition, @@ -209,6 +210,16 @@ impl ControlFlowGraph { let block2_under_success = block2_under_success.contains(&block2_index); block1_under_success == block2_under_success } + + fn back_edges(&self, content: &ir::FunctionDefinition) -> Vec<(usize, usize)> { + let mut result = Vec::new(); + depth_first_search(&self.content(content).graph, [0.into()], |event| { + if let DfsEvent::BackEdge(from, to) = event { + result.push((from.index(), to.index())) + } + }); + result + } } #[derive(Debug)] @@ -282,6 +293,9 @@ impl<'item, 'bind: 'item> BindedControlFlowGraph<'item, 'bind> { block2_index, ) } + pub fn back_edges(&self) -> Vec<(usize, usize)> { + self.item.back_edges(&self.bind_on) + } } impl<'item, 'bind: 'item> IsAnalyzer<'item, 'bind> for ControlFlowGraph { diff --git a/src/ir/editor/analyzer/control_flow/structural/mod.rs b/src/ir/editor/analyzer/control_flow/structural/mod.rs new file mode 100644 index 0000000..b18c62e --- /dev/null +++ b/src/ir/editor/analyzer/control_flow/structural/mod.rs @@ -0,0 +1,745 @@ +use std::{collections::HashMap, fmt}; + +use itertools::Itertools; +use petgraph::{ + algo::all_simple_paths, + prelude::NodeIndex, + stable_graph::StableDiGraph, + visit::{depth_first_search, DfsEvent, DfsPostOrder, EdgeRef}, + Direction, +}; + +use super::BindedControlFlowGraph; + +mod selector; + +#[derive(Clone, Debug, Default)] +struct FoldedCFG { + graph: StableDiGraph, usize>, + entry: NodeIndex, +} + +impl fmt::Display for FoldedCFG { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(e) = self.graph.node_weight(self.entry) { + write!(f, "{}", e)?; + } else { + write!(f, "??")?; + } + for n in self.graph.node_indices() { + if n != self.entry { + write!(f, ", {}", self.graph[n])?; + } + } + Ok(()) + } +} + +fn back_edges(c: &FoldedCFG) -> Vec<(NodeIndex, NodeIndex)> { + let mut result = Vec::new(); + depth_first_search(&c.graph, Some(c.entry), |event| { + if let DfsEvent::BackEdge(from, to) = event { + result.push((from, to)) + } + }); + result +} + +#[derive(Clone, Debug)] +struct If { + content: FoldedCFG, + on_then: NodeIndex, + on_else: Option>, +} + +#[derive(Clone, Debug)] +enum RegionNode { + Single(usize), + Loop(FoldedCFG), + Block(FoldedCFG), + If(If), +} + +impl fmt::Display for RegionNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RegionNode::Single(bb_index) => write!(f, "{bb_index}"), + RegionNode::Loop(content) => { + write!(f, "loop({}", content.graph[content.entry])?; + for n in content.graph.node_indices() { + if n != content.entry { + write!(f, ", {}", content.graph[n])?; + } + } + write!(f, ")") + } + RegionNode::Block(content) => { + write!(f, "block({}", content.graph[content.entry])?; + for n in content.graph.node_indices() { + if n != content.entry { + write!(f, ", {}", content.graph[n])?; + } + } + write!(f, ")") + } + RegionNode::If(content) => { + write!( + f, + "if ({}) then ", + content.content.graph[content.content.entry] + )?; + if let Some(nn) = content.content.graph.node_weight(content.on_then) { + write!(f, "({})", nn)?; + } else { + write!(f, "(??{})", content.on_then.index())?; + } + if let Some(on_else) = content.on_else { + if let Some(nn) = content.content.graph.node_weight(on_else) { + write!(f, " else ({})", nn)?; + } else { + write!(f, " else (??{})", on_else.index())?; + } + } + Ok(()) + } + } + } +} + +impl FoldedCFG { + fn fold_acyclic( + content: &mut FoldedCFG, + cfg: &BindedControlFlowGraph, + node: NodeIndex, + ) -> bool { + let nodes = single_entry_single_exit_nodes(content, node); + if nodes.len() >= 2 { + fold_block(&nodes, content) + } else if content + .graph + .neighbors_directed(node, Direction::Outgoing) + .count() + == 2 + { + fold_if_else(content, node, cfg) + } else { + // todo!("switch ... etc") + false + } + } + + fn fold_cyclic(content: &mut FoldedCFG, node: NodeIndex) -> bool { + let backedges = back_edges(content); + let backedges_into_node = backedges.into_iter().filter(|(_, to)| *to == node); + let pathes_into_node = backedges_into_node.map(|(backedge_source, _)| { + all_simple_paths::, _>(&content.graph, node, backedge_source, 0, None) + .flatten() + .collect_vec() + }); + let smallest_loop_content = pathes_into_node.min_by(|a, b| Ord::cmp(&a.len(), &b.len())); + if let Some(mut smallest_loop_content) = smallest_loop_content { + smallest_loop_content.sort(); + smallest_loop_content.dedup(); + let mut loop_subgraph = FoldedCFG::default(); + let mut node_index_map = HashMap::new(); // id in old => id in new + for loop_content_index in &smallest_loop_content { + let loop_content_node = &content.graph[loop_content_index.clone()]; + node_index_map.insert( + loop_content_index, + loop_subgraph.graph.add_node(loop_content_node.clone()), + ); + } + loop_subgraph.entry = node_index_map[&node]; + let mut global_incoming_edges: HashMap<_, Vec<_>> = HashMap::new(); // source => weight + let mut global_outgoing_edges: HashMap<_, Vec<_>> = HashMap::new(); + for loop_content_index in &smallest_loop_content { + let incoming_edges = content + .graph + .edges_directed(loop_content_index.clone(), Direction::Incoming) + .into_iter(); + let (intern_edges, extern_edges) = incoming_edges + .partition::, _>(|it| node_index_map.contains_key(&it.source())); + for intern_edge in intern_edges { + let intern_from = node_index_map.get(&intern_edge.source()).unwrap(); + let intern_target = node_index_map.get(&intern_edge.target()).unwrap(); + loop_subgraph.graph.add_edge( + intern_from.clone(), + intern_target.clone(), + intern_edge.weight().clone(), + ); + } + for extern_edge in extern_edges { + global_incoming_edges + .entry(extern_edge.source()) + .or_default() + .extend(extern_edge.weight().clone()); + } + + let outgoing_edges = content + .graph + .edges_directed(loop_content_index.clone(), Direction::Outgoing) + .into_iter(); + let (_intern_edges, extern_edges) = outgoing_edges + .partition::, _>(|it| node_index_map.contains_key(&it.target())); + // no need to handle `intern_edges`, because they will be handled of `incoming_edges` on the other side + for extern_edge in extern_edges { + global_outgoing_edges + .entry(extern_edge.target()) + .or_default() + .extend(extern_edge.weight().clone()); + } + } + let new_node = content.graph.add_node(RegionNode::Loop(loop_subgraph)); + for (from, weight) in global_incoming_edges { + content.graph.add_edge(from, new_node, weight); + } + for (to, weight) in global_outgoing_edges { + content.graph.add_edge(new_node, to, weight); + } + for node_to_remove in smallest_loop_content.iter().rev() { + content.graph.remove_node(*node_to_remove); + } + if smallest_loop_content.contains(&content.entry) { + content.entry = new_node; + } + true + } else { + false + } + } + + pub fn structural_analysis(mut self, cfg: &BindedControlFlowGraph) -> FoldedCFG { + 'outer: loop { + let mut result = self.clone(); + let mut dfs = DfsPostOrder::new(&self.graph, self.entry); + while let Some(node) = dfs.next(&self.graph) { + if Self::fold_acyclic(&mut result, cfg, node) { + self = result; + continue 'outer; + } + if Self::fold_cyclic(&mut result, node) { + self = result; + continue 'outer; + } + } + break; + } + self + } + pub fn from_control_flow_graph(graph: &BindedControlFlowGraph) -> FoldedCFG { + let graph_ = graph.graph(); + let mut result = graph_.map( + |node_index, _| RegionNode::Single(node_index.index()), + |edge_id, _| { + let (from, to) = graph_.edge_endpoints(edge_id).unwrap(); + vec![(from.index(), to.index())] + }, + ); + result.remove_node(graph.bind_on.content.len().into()); + FoldedCFG { + graph: result.into(), + entry: 0.into(), + } + } +} + +fn fold_if_else( + region_content: &mut FoldedCFG, + condition: NodeIndex, + cfg: &BindedControlFlowGraph, +) -> bool { + let (outgoing_node_a, outgoing_node_b) = + outgoing_nodes_from_condition(region_content, condition, cfg); + println!( + "fold_if_else on {}, cond={}, a_side={}, b_side={}", + region_content, + region_content.graph[condition], + region_content.graph[outgoing_node_a], + region_content.graph[outgoing_node_b], + ); + let subregion_node = ®ion_content.graph[condition]; + let subregion_a = ®ion_content.graph[outgoing_node_a].clone(); + let mut new_region_content = FoldedCFG { + graph: StableDiGraph::with_capacity(3, 2), + entry: 0.into(), + }; + let inserted_subregion_node_index = new_region_content.graph.add_node(subregion_node.clone()); + new_region_content.entry = inserted_subregion_node_index; + let after_a = region_content + .graph + .neighbors_directed(outgoing_node_a, Direction::Outgoing) + .collect_vec(); + print!("after_a: "); + for aa in &after_a { + print!("{}, ", region_content.graph[aa.clone()]); + } + println!(); + let after_b = region_content + .graph + .neighbors_directed(outgoing_node_b, Direction::Outgoing) + .collect_vec(); + print!("after_b: "); + for bb in &after_b { + print!("{}, ", region_content.graph[bb.clone()]); + } + println!(); + let edges_into_node = region_content + .graph + .edges_directed(condition, Direction::Incoming) + .map(|it| (it.source(), it.weight().clone())) + .collect_vec(); + if after_a.contains(&outgoing_node_b) { + // if, without an else + println!("if, without an else"); + // the content should only contains the condition (`node`) and the then part (`a`) + let inserted_subregion_a_index = new_region_content.graph.add_node(subregion_a.clone()); + let new_if = If { + content: new_region_content, + on_then: inserted_subregion_a_index, + on_else: None, + }; + // create a new node containing all nodes in `nodes` + let new_node = region_content.graph.add_node(RegionNode::If(new_if)); + // redirect in edges + for edge_into_node in edges_into_node { + region_content + .graph + .add_edge(edge_into_node.0, new_node, edge_into_node.1.clone()); + } + // redirect out edges + let condition_to_b = region_content + .graph + .find_edge(condition, outgoing_node_b) + .unwrap(); + let condition_to_b = ®ion_content.graph[condition_to_b]; + let a_to_b = region_content + .graph + .find_edge(outgoing_node_a, outgoing_node_b) + .unwrap(); + let a_to_b = ®ion_content.graph[a_to_b]; + let _new_edge = region_content.graph.add_edge( + new_node, + outgoing_node_b, + Iterator::chain(condition_to_b.into_iter(), a_to_b.into_iter()) + .cloned() + .collect(), + ); + // remove nodes in `nodes` + region_content + .graph + .retain_nodes(|_, n| n != condition && n != outgoing_node_a); + true + } else if after_a.len() == 1 && after_a == after_b { + let c = after_a[0]; + // if, with an else + println!("if, with an else"); + let subregion_b = ®ion_content.graph[outgoing_node_b].clone(); + let inserted_subregion_a_index = new_region_content.graph.add_node(subregion_a.clone()); + let inserted_subregion_b_index = new_region_content.graph.add_node(subregion_b.clone()); + println!( + "{}: subregion_a={} == {}", + inserted_subregion_a_index.index(), + subregion_a, + &new_region_content.graph[inserted_subregion_a_index], + ); + println!( + "{}: subregion_b={} == {}", + inserted_subregion_b_index.index(), + subregion_b, + &new_region_content.graph[inserted_subregion_b_index], + ); + let new_if = If { + content: new_region_content, + on_then: inserted_subregion_a_index, + on_else: Some(inserted_subregion_b_index), + }; + // create a new node containing all nodes in `nodes` + let new_node = region_content.graph.add_node(RegionNode::If(new_if)); + // redirect in edges + for edge_into_node in edges_into_node { + region_content + .graph + .add_edge(edge_into_node.0, new_node, edge_into_node.1.clone()); + } + // redirect out edges + let (a_to_c, b_to_c) = region_content + .graph + .edges_directed(outgoing_node_a, Direction::Outgoing) + .chain( + region_content + .graph + .edges_directed(outgoing_node_b, Direction::Outgoing), + ) + .collect_tuple() + .unwrap(); + let new_weight = Iterator::chain(a_to_c.weight().into_iter(), b_to_c.weight().into_iter()) + .cloned() + .collect(); + let _new_edge = region_content.graph.add_edge(new_node, c, new_weight); + // remove nodes in `nodes` + region_content + .graph + .retain_nodes(|_, n| n != condition && n != outgoing_node_a && n != outgoing_node_b); + if condition == region_content.entry { + region_content.entry = new_node; + } + true + } else { + false + } +} + +fn outgoing_nodes_from_condition( + region_content: &FoldedCFG, + condition: NodeIndex, + cfg: &BindedControlFlowGraph, +) -> (NodeIndex, NodeIndex) { + let (mut outgoing_node_a, mut outgoing_node_b) = region_content + .graph + .neighbors_directed(condition, Direction::Outgoing) + .collect_tuple() + .unwrap(); + if region_content + .graph + .neighbors_directed(outgoing_node_b, Direction::Outgoing) + .contains(&outgoing_node_a) + { + // we make sure `outgoing_node_a` is at least as high as `outgoing_node_b` + (outgoing_node_a, outgoing_node_b) = (outgoing_node_b, outgoing_node_a); + } else { + // we make sure `outgoing_node_a` is on the `then` part and `outgoing_node_b` is on the else part + let edge_in_origin_graph_node_to_a = region_content + .graph + .edges_connecting(condition, outgoing_node_a) + .exactly_one() + .unwrap() + .weight() + .first() + .unwrap(); + if !cfg.branch_direction( + edge_in_origin_graph_node_to_a.0, + edge_in_origin_graph_node_to_a.1, + ) { + // a is not on the "then" side, swap them + (outgoing_node_a, outgoing_node_b) = (outgoing_node_b, outgoing_node_a); + } + } + (outgoing_node_a, outgoing_node_b) +} + +fn single_entry_single_exit_nodes( + content: &FoldedCFG, + node: NodeIndex, +) -> Vec> { + let mut result = Vec::new(); + let mut current_looking_at_node = node; + // detect up + while content + .graph + .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .count() + == 1 + && content + .graph + .neighbors_directed(current_looking_at_node.into(), Direction::Outgoing) + .count() + == 1 + { + result.push(current_looking_at_node); + current_looking_at_node = content + .graph + .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .exactly_one() + .unwrap(); + } + result.reverse(); + result.pop(); + current_looking_at_node = node; + // detect down + while content + .graph + .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .count() + == 1 + && content + .graph + .neighbors_directed(current_looking_at_node.into(), Direction::Outgoing) + .count() + == 1 + { + result.push(current_looking_at_node); + current_looking_at_node = content + .graph + .neighbors_directed(current_looking_at_node, Direction::Outgoing) + .exactly_one() + .unwrap(); + } + result +} + +fn fold_block(nodes: &[NodeIndex], region_content: &mut FoldedCFG) -> bool { + if nodes.len() < 2 { + return false; + } + let mut new_subregion_content = FoldedCFG { + graph: StableDiGraph::with_capacity(nodes.len(), nodes.len() - 1), + entry: 0.into(), + }; + let mut last_node_index = None; + let mut last_inserted = None; + for node in nodes { + let sub_region_in_origin = region_content.graph[*node].clone(); + let current_inserted = new_subregion_content.graph.add_node(sub_region_in_origin); + if let Some(last_inserted) = last_inserted { + let last_node_index = last_node_index.unwrap(); + let origin_edge_weight = region_content + .graph + .edges_connecting(last_node_index, *node) + .exactly_one() + .unwrap() + .weight(); + new_subregion_content.graph.add_edge( + last_inserted, + current_inserted, + origin_edge_weight.clone(), + ); + } + last_node_index = Some(*node); + last_inserted = Some(current_inserted); + } + let edges_into_the_new_node = region_content + .graph + .edges_directed(nodes[0], Direction::Incoming) + .map(|e| (e.source(), e.target(), e.weight().clone())) + .collect_vec(); + let edges_out_of_the_new_node = region_content + .graph + .edges_directed(*nodes.last().unwrap(), Direction::Outgoing) + .map(|e| (e.source(), e.target(), e.weight().clone())) + .collect_vec(); + // create a new node containing all nodes in `nodes` + let new_node = region_content + .graph + .add_node(RegionNode::Block(new_subregion_content)); + // re-point all edges into the new node to the new node + for edge_into_the_new_node in edges_into_the_new_node { + region_content.graph.add_edge( + edge_into_the_new_node.0, + new_node, + edge_into_the_new_node.2.clone(), + ); + } + // re-point all edges out of the new node from the new node + for edge_out_of_the_new_node in edges_out_of_the_new_node { + region_content.graph.add_edge( + new_node, + edge_out_of_the_new_node.1, + edge_out_of_the_new_node.2.clone(), + ); + } + // remove nodes in `nodes` + region_content + .graph + .retain_nodes(|_, n| !nodes.contains(&n)); + true +} + +#[cfg(test)] +mod tests { + use crate::{ + ir::{ + self, + analyzer::{ControlFlowGraph, IsAnalyzer}, + function::test_util::*, + FunctionDefinition, + }, + utility::data_type, + }; + + use super::*; + + #[test] + fn test_fold_cyclic_self() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + graph.add_edge(node_0, node_0, vec![(0, 0)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + + FoldedCFG::fold_cyclic(&mut graph, 0.into()); + dbg!(graph); + } + #[test] + fn test_fold_cyclic_two() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + let node_1 = graph.add_node(RegionNode::Single(1)); + graph.add_edge(node_0, node_1, vec![(0, 1)]); + graph.add_edge(node_1, node_0, vec![(1, 0)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + dbg!(back_edges(&graph)); + + FoldedCFG::fold_cyclic(&mut graph /*,cfg */, 0.into()); + } + #[test] + fn test_fold_cyclic_three() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + let node_1 = graph.add_node(RegionNode::Single(1)); + let node_2 = graph.add_node(RegionNode::Single(2)); + let node_3 = graph.add_node(RegionNode::Single(3)); + graph.add_edge(node_0, node_1, vec![(0, 1)]); + graph.add_edge(node_1, node_2, vec![(1, 2)]); + graph.add_edge(node_2, node_0, vec![(2, 0)]); + graph.add_edge(node_1, node_3, vec![(1, 3)]); + graph.add_edge(node_2, node_3, vec![(2, 3)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + + FoldedCFG::fold_cyclic(&mut graph, 0.into()); + dbg!(graph); + } + #[test] + fn test_fold_cyclic_three_2() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + let node_1 = graph.add_node(RegionNode::Single(1)); + let node_2 = graph.add_node(RegionNode::Single(2)); + graph.add_edge(node_0, node_1, vec![(0, 1)]); + graph.add_edge(node_1, node_0, vec![(1, 0)]); + graph.add_edge(node_1, node_2, vec![(1, 2)]); + graph.add_edge(node_2, node_0, vec![(2, 0)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + + FoldedCFG::fold_cyclic(&mut graph, 0.into()); + dbg!(graph); + } + + #[test] + fn test_fold_cyclic_four() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + let node_1 = graph.add_node(RegionNode::Single(1)); + let node_2 = graph.add_node(RegionNode::Single(2)); + let node_3 = graph.add_node(RegionNode::Single(3)); + graph.add_edge(node_0, node_1, vec![(0, 1)]); + graph.add_edge(node_1, node_0, vec![(1, 0)]); + graph.add_edge(node_1, node_2, vec![(1, 2)]); + graph.add_edge(node_2, node_0, vec![(2, 0)]); + graph.add_edge(node_2, node_3, vec![(2, 3)]); + graph.add_edge(node_3, node_0, vec![(3, 0)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + FoldedCFG::fold_cyclic(&mut graph, 0.into()); + dbg!(graph); + } + + #[test] + fn test_fold_cyclic_seq() { + let mut graph = StableDiGraph::default(); + let node_0 = graph.add_node(RegionNode::Single(0)); + let node_1 = graph.add_node(RegionNode::Single(1)); + let node_2 = graph.add_node(RegionNode::Single(2)); + let node_3 = graph.add_node(RegionNode::Single(3)); + let node_4 = graph.add_node(RegionNode::Single(4)); + graph.add_edge(node_0, node_1, vec![(0, 1)]); + graph.add_edge(node_1, node_2, vec![(1, 2)]); + graph.add_edge(node_2, node_3, vec![(2, 3)]); + graph.add_edge(node_3, node_1, vec![(3, 1)]); + graph.add_edge(node_3, node_4, vec![(3, 4)]); + let mut graph = FoldedCFG { + graph, + entry: node_0, + }; + FoldedCFG::fold_cyclic(&mut graph, node_1); + dbg!(graph); + } + + #[test] + fn test_structual_analysis() { + let function_definition = FunctionDefinition { + header: ir::FunctionHeader { + name: "f".to_string(), + parameters: Vec::new(), + return_type: data_type::Type::None, + }, + content: vec![ + branch_block(0, 1, 2), + jump_block(1, 3), + jump_block(2, 6), + jump_block(3, 4), + jump_block(4, 5), + branch_block(5, 3, 6), + ret_block(6), + ], + }; + let control_flow_graph = ControlFlowGraph::new(); + let binded = control_flow_graph.bind(&function_definition); + dbg!(&binded); + let folded_cfg = FoldedCFG::from_control_flow_graph(&binded); + let saed = FoldedCFG::structural_analysis(folded_cfg, &binded); + println!("{}", saed); + } + + #[test] + fn test_structual_analysis2() { + let function_definition = FunctionDefinition { + header: ir::FunctionHeader { + name: "f".to_string(), + parameters: Vec::new(), + return_type: data_type::Type::None, + }, + content: vec![ + jump_block(0, 1), + branch_block(1, 2, 3), + jump_block(2, 3), + ret_block(3), + ], + }; + let control_flow_graph = ControlFlowGraph::new(); + let binded = control_flow_graph.bind(&function_definition); + let folded_cfg = FoldedCFG::from_control_flow_graph(&binded); + let saed = FoldedCFG::structural_analysis(folded_cfg, &binded); + println!("{}", saed); + } + + #[test] + fn test_structual_analysis3() { + let function_definition = FunctionDefinition { + header: ir::FunctionHeader { + name: "f".to_string(), + parameters: Vec::new(), + return_type: data_type::Type::None, + }, + content: vec![ + jump_block(0, 1), + branch_block(1, 2, 6), + branch_block(2, 3, 4), + jump_block(3, 5), + jump_block(5, 1), + jump_block(4, 5), + branch_block(6, 7, 8), + jump_block(7, 9), + jump_block(8, 9), + ret_block(9), + ], + }; + let control_flow_graph = ControlFlowGraph::new(); + let binded = control_flow_graph.bind(&function_definition); + let folded_cfg = FoldedCFG::from_control_flow_graph(&binded); + let saed = FoldedCFG::structural_analysis(folded_cfg, &binded); + println!("{}", saed); + } +} diff --git a/src/ir/editor/analyzer/control_flow/structural/selector.rs b/src/ir/editor/analyzer/control_flow/structural/selector.rs new file mode 100644 index 0000000..0a0aeb8 --- /dev/null +++ b/src/ir/editor/analyzer/control_flow/structural/selector.rs @@ -0,0 +1,234 @@ +use delegate::delegate; +use std::{cmp::Ordering, collections::VecDeque, fmt, num::ParseIntError, str::FromStr}; +#[derive(Clone, PartialEq, Eq)] +pub(super) enum CFSelectorSegment { + ContentAtIndex(usize), + IfCondition, + IndexInSuccess(usize), + IndexInFailure(usize), +} + +impl fmt::Display for CFSelectorSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CFSelectorSegment::ContentAtIndex(index) => write!(f, "{}", index), + CFSelectorSegment::IfCondition => write!(f, "if_condition"), + CFSelectorSegment::IndexInSuccess(index) => write!(f, "success->{}", index), + CFSelectorSegment::IndexInFailure(index) => write!(f, "failure->{}", index), + } + } +} + +impl fmt::Debug for CFSelectorSegment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl FromStr for CFSelectorSegment { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + if s == "if_condition" { + Ok(CFSelectorSegment::IfCondition) + } else if s.starts_with("success->") { + let index_str = s.strip_prefix("success->").unwrap(); + let value = index_str.parse()?; + Ok(CFSelectorSegment::IndexInSuccess(value)) + } else if s.starts_with("failure->") { + let index_str = s.strip_prefix("failure->").unwrap(); + let value = index_str.parse()?; + Ok(CFSelectorSegment::IndexInFailure(value)) + } else { + let value = s.parse()?; + Ok(CFSelectorSegment::ContentAtIndex(value)) + } + } +} + +impl PartialOrd for CFSelectorSegment { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (CFSelectorSegment::ContentAtIndex(i), CFSelectorSegment::ContentAtIndex(j)) + | (CFSelectorSegment::IndexInSuccess(i), CFSelectorSegment::IndexInSuccess(j)) + | (CFSelectorSegment::IndexInFailure(i), CFSelectorSegment::IndexInFailure(j)) => { + i.partial_cmp(j) + } + (CFSelectorSegment::IfCondition, CFSelectorSegment::IfCondition) => { + Some(Ordering::Equal) + } + ( + CFSelectorSegment::IfCondition, + CFSelectorSegment::IndexInSuccess(_) | CFSelectorSegment::IndexInFailure(_), + ) => Some(Ordering::Less), + ( + CFSelectorSegment::IndexInFailure(_) | CFSelectorSegment::IndexInSuccess(_), + CFSelectorSegment::IfCondition, + ) => Some(Ordering::Greater), + (CFSelectorSegment::IndexInSuccess(_), CFSelectorSegment::IndexInFailure(_)) => { + Some(Ordering::Less) + } + (CFSelectorSegment::IndexInFailure(_), CFSelectorSegment::IndexInSuccess(_)) => { + Some(Ordering::Greater) + } + _ => None, + } + } +} + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct CFSelector(VecDeque); + +impl fmt::Display for CFSelector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "@"); + if self.0.is_empty() { + write!(f, "()") + } else { + write!( + f, + "{}", + self.0 + .iter() + .map(|s| format!("{}", s)) + .collect::>() + .join("/") + ) + } + } +} + +impl fmt::Debug for CFSelector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl FromStr for CFSelector { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + debug_assert_eq!(s.chars().next().unwrap(), '@'); + let s = &s[1..]; + let mut result = VecDeque::new(); + let parts = s.split('/'); + for next_part in parts { + let segment = CFSelectorSegment::from_str(next_part)?; + result.push_back(segment); + } + Ok(Self(result)) + } +} + +impl CFSelector { + pub fn new_empty() -> Self { + Self(VecDeque::new()) + } + + pub fn from_segment(segment: CFSelectorSegment) -> Self { + let mut result = VecDeque::new(); + result.push_back(segment); + Self(result) + } + + delegate! { + to self.0 { + pub fn is_empty(&self) -> bool; + pub fn len(&self) -> usize; + pub fn pop_front(&mut self) -> Option; + pub fn pop_back(&mut self) -> Option; + pub fn front(&self) -> Option<&CFSelectorSegment>; + pub fn back(&self) -> Option<&CFSelectorSegment>; + pub fn push_front(&mut self, segment: CFSelectorSegment); + pub fn push_back(&mut self, segment: CFSelectorSegment); + } + } + + pub fn parent(&self) -> Option { + let mut result = self.0.clone(); + if result.is_empty() { + None + } else { + result.pop_back(); + Some(CFSelector(result)) + } + } + + pub fn split_first(mut self) -> Option<(CFSelectorSegment, CFSelector)> { + let front = self.0.pop_front()?; + Some((front, self)) + } + + pub fn split_last(mut self) -> Option<(CFSelector, CFSelectorSegment)> { + let back = self.0.pop_back()?; + Some((self, back)) + } + + // fn is_ancestor_of(&self, other: &CFSelector) -> bool { + // for (from_self, from_other) in zip(&self.0, &other.0) { + // if from_self != from_other { + // return false; + // } + // } + // true + // } + + // pub fn is_parent_of(&self, other: &CFSelector) -> bool { + // // different from direct parent relationship, this function consider, for example, `0`.is_parent_of(`0/if_condition/0`) + // let lca = self.lowest_common_ancestor(other); + // let parent_rest = self.range(lca.len()..); + // let mut child_rest = other.range(lca.len()..); + // if !parent_rest.is_empty() || child_rest.is_empty() { + // return false; + // } + // while child_rest.len() > 1 { + // let front = child_rest.pop_front().unwrap(); + // if front == CFSelectorSegment::IfCondition { + // return false; + // } + // } + // true + // } + + // pub(super) fn merge(&self, other: &CFSelector) -> CFSelector { + // let mut result = self.clone(); + // result.0.extend(other.0.clone()); + // result + // } + + // pub fn is_if_condition(&self) -> bool { + // matches!(self.back(), Some(CFSelectorSegment::IfCondition)) + // } + + // pub fn lowest_common_ancestor(&self, other: &CFSelector) -> CFSelector { + // let mut result = Self::new_empty(); + // for (from_self, from_other) in zip(&self.0, &other.0) { + // if from_self == from_other { + // result.0.push_back(from_self.clone()); + // } else { + // break; + // } + // } + // result + // } + + // pub fn range>(&self, range: R) -> Self { + // Self(self.0.range(range).cloned().collect()) + // } + + // pub fn is_sibling(selector: &CFSelector, last_selector: &CFSelector) -> bool { + // if selector.len() != last_selector.len() { + // false + // } else { + // let shared_part = Self::lowest_common_ancestor(selector, last_selector); + // shared_part.len() == selector.len() - 1 + // } + // } + + // pub fn block_like_count(&self) -> usize { + // self.0 + // .iter() + // .filter(|it| matches!(it, CFSelectorSegment::ContentAtIndex(_))) + // .count() + // } +} diff --git a/src/ir/function/mod.rs b/src/ir/function/mod.rs index 6837974..88d874d 100644 --- a/src/ir/function/mod.rs +++ b/src/ir/function/mod.rs @@ -276,8 +276,18 @@ pub fn formalize(mut function: FunctionDefinition) -> FunctionDefinition { for (this_index, next_index) in (0..function.content.len()).tuple_windows() { let next_item_name = function.content[next_index].name.clone().unwrap(); let this = &mut function.content[this_index]; - if let Some(last) = this.content.last() && !matches!(last, IRStatement::Jump(_) | IRStatement::Branch(_) | IRStatement::Ret(_)) { - this.content.push(Jump { label: next_item_name }.into()) + if let Some(last) = this.content.last() + && !matches!( + last, + IRStatement::Jump(_) | IRStatement::Branch(_) | IRStatement::Ret(_) + ) + { + this.content.push( + Jump { + label: next_item_name, + } + .into(), + ) } } function diff --git a/src/ir/function/statement/branch.rs b/src/ir/function/statement/branch.rs index 05b8001..74fde31 100644 --- a/src/ir/function/statement/branch.rs +++ b/src/ir/function/statement/branch.rs @@ -99,10 +99,14 @@ impl Branch { impl IsIRStatement for Branch { fn on_register_change(&mut self, from: &RegisterName, to: Quantity) { - if let Quantity::RegisterName(operand1) = &mut self.operand1 && operand1 == from { + if let Quantity::RegisterName(operand1) = &mut self.operand1 + && operand1 == from + { self.operand1 = to.clone(); } - if let Quantity::RegisterName(operand2) = &mut self.operand2 && operand2 == from { + if let Quantity::RegisterName(operand2) = &mut self.operand2 + && operand2 == from + { self.operand2 = to; } } diff --git a/src/ir/function/statement/calculate/binary.rs b/src/ir/function/statement/calculate/binary.rs index b67a0fb..53b1ae4 100644 --- a/src/ir/function/statement/calculate/binary.rs +++ b/src/ir/function/statement/calculate/binary.rs @@ -115,10 +115,14 @@ pub struct BinaryCalculate { impl IsIRStatement for BinaryCalculate { fn on_register_change(&mut self, from: &RegisterName, to: Quantity) { - if let Quantity::RegisterName(op1) = &self.operand1 && op1 == from { + if let Quantity::RegisterName(op1) = &self.operand1 + && op1 == from + { self.operand1 = to.clone(); } - if let Quantity::RegisterName(op2) = &self.operand2 && op2 == from { + if let Quantity::RegisterName(op2) = &self.operand2 + && op2 == from + { self.operand2 = to.clone(); } if &self.to == from { diff --git a/src/ir/function/statement/calculate/unary.rs b/src/ir/function/statement/calculate/unary.rs index 169772a..90e5c7a 100644 --- a/src/ir/function/statement/calculate/unary.rs +++ b/src/ir/function/statement/calculate/unary.rs @@ -61,7 +61,9 @@ pub struct UnaryCalculate { impl IsIRStatement for UnaryCalculate { fn on_register_change(&mut self, from: &RegisterName, to: Quantity) { - if let Quantity::RegisterName(operand) = &self.operand && operand == from { + if let Quantity::RegisterName(operand) = &self.operand + && operand == from + { self.operand = to.clone(); } if &self.to == from { diff --git a/src/ir/function/statement/call.rs b/src/ir/function/statement/call.rs index e7db344..38ddd6a 100644 --- a/src/ir/function/statement/call.rs +++ b/src/ir/function/statement/call.rs @@ -36,7 +36,9 @@ pub struct Call { impl IsIRStatement for Call { fn on_register_change(&mut self, from: &RegisterName, to: Quantity) { - if let Some(result_to) = &self.to && result_to == from { + if let Some(result_to) = &self.to + && result_to == from + { self.to = Some(to.clone().unwrap_local()); } for param in self.params.iter_mut() { diff --git a/src/ir/optimize/pass/topological_sort.rs b/src/ir/optimize/pass/topological_sort.rs index d73d1cf..e0208f9 100644 --- a/src/ir/optimize/pass/topological_sort.rs +++ b/src/ir/optimize/pass/topological_sort.rs @@ -71,7 +71,9 @@ fn topological_order_dfs( return 2 + at_index; } // we should visit all nodes in this loop before the others - if let Some(in_loop) = &in_loop && in_loop.contains(to_visit_node.index()) { + if let Some(in_loop) = &in_loop + && in_loop.contains(to_visit_node.index()) + { return 1; } 0 From ecc37aa8e335654613cb4991a87ff4f1281657c8 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Thu, 27 Jun 2024 23:25:16 +0200 Subject: [PATCH 3/3] structural analysis --- .github/workflows/deploy-doc.yml | 61 +-- Cargo.toml | 5 +- crates/ir-breadboard/Cargo.toml | 17 + crates/ir-breadboard/src/lib.rs | 161 +++++++ doc/content/tools/ir_breadboard.md | 409 ++++++++++++++++++ doc/static/tools/ir-breadboard/.gitignore | 4 + .../param_transformer/branch_high.rs | 6 + .../param_transformer/branch_low.rs | 6 + .../param_transformer/csr.rs | 6 + .../param_transformer/jal_form.rs | 6 + .../param_transformer/register.rs | 6 + src/ir/editor/analyzer/control_flow/mod.rs | 4 +- .../analyzer/control_flow/structural/mod.rs | 122 +++--- src/ir/editor/analyzer/mod.rs | 2 +- src/ir/function/basic_block.rs | 12 +- src/ir/function/mod.rs | 18 +- src/ir/function/parameter.rs | 4 +- src/ir/function/statement/alloca.rs | 4 +- src/ir/function/statement/branch.rs | 8 +- src/ir/function/statement/calculate/binary.rs | 6 +- src/ir/function/statement/calculate/unary.rs | 6 +- src/ir/function/statement/call.rs | 4 +- src/ir/function/statement/jump.rs | 4 +- src/ir/function/statement/load.rs | 4 +- src/ir/function/statement/load_field.rs | 6 +- src/ir/function/statement/mod.rs | 12 +- src/ir/function/statement/phi.rs | 6 +- src/ir/function/statement/ret.rs | 4 +- src/ir/function/statement/set_field.rs | 4 +- src/ir/function/statement/store.rs | 4 +- src/ir/global_definition.rs | 3 +- src/ir/integer_literal.rs | 3 +- src/ir/mod.rs | 26 +- src/ir/optimize/pass/fix_irreducible/mod.rs | 10 +- src/ir/optimize/pass/memory_to_register.rs | 6 +- src/ir/optimize/pass/mod.rs | 6 +- .../pass/remove_load_directly_after_store.rs | 6 +- .../optimize/pass/remove_only_once_store.rs | 4 +- .../optimize/pass/remove_unused_register.rs | 7 +- src/ir/optimize/pass/topological_sort.rs | 6 +- src/ir/quantity/global.rs | 3 +- src/ir/quantity/local.rs | 3 +- src/ir/quantity/mod.rs | 4 +- src/ir/type_definition.rs | 3 +- src/utility/data_type.rs | 5 +- 45 files changed, 855 insertions(+), 161 deletions(-) create mode 100644 crates/ir-breadboard/Cargo.toml create mode 100644 crates/ir-breadboard/src/lib.rs create mode 100644 doc/content/tools/ir_breadboard.md create mode 100644 doc/static/tools/ir-breadboard/.gitignore diff --git a/.github/workflows/deploy-doc.yml b/.github/workflows/deploy-doc.yml index fcb356d..f604c1b 100644 --- a/.github/workflows/deploy-doc.yml +++ b/.github/workflows/deploy-doc.yml @@ -2,7 +2,7 @@ name: Build and Deploy doc on: push: - branches: [ main ] + branches: [main] env: CARGO_TERM_COLOR: always @@ -24,30 +24,35 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 - - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - name: Build wasm packages - working-directory: ./crates/control-flow-graph-wasm - run: wasm-pack build --target web --release - - name: Move wasm packages - run: mv -f ./crates/control-flow-graph-wasm/pkg/* ./doc/static/tools/graph-editor/ - - name: Build site - run: docker run -u "$(id -u):$(id -g)" -v $PWD/doc:/app --workdir /app ghcr.io/getzola/zola:v0.16.0 build - - name: Build crate doc - run: cargo doc --workspace --document-private-items --all-features -r - - name: Move crate doc - run: sudo mv ./target/doc/* ./doc/public/ - - name: Setup Pages - uses: actions/configure-pages@v2 - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - with: - path: './doc/public' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Build wasm packages + working-directory: ./crates/control-flow-graph-wasm + run: wasm-pack build --target web --release + - name: Build wasm packages + working-directory: ./crates/ir-breadboard + run: wasm-pack build --target web --release + - name: Move wasm packages + run: mv -f ./crates/control-flow-graph-wasm/pkg/* ./doc/static/tools/graph-editor/ + - name: Move wasm packages + run: mv -f ./crates/ir-breadboard/pkg/* ./doc/static/tools/ir-breadboard/ + - name: Build site + run: docker run -u "$(id -u):$(id -g)" -v $PWD/doc:/app --workdir /app ghcr.io/getzola/zola:v0.16.0 build + - name: Build crate doc + run: cargo doc --workspace --document-private-items --all-features -r + - name: Move crate doc + run: sudo mv ./target/doc/* ./doc/public/ + - name: Setup Pages + uses: actions/configure-pages@v2 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: "./doc/public" + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/Cargo.toml b/Cargo.toml index b50a161..9d7c11b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ indexmap = "2.1.0" itertools = "0.13.0" nom = "7.1.3" paste = "1.0.14" -petgraph = "0.6.4" +petgraph = { version = "0.6.4", features = ["serde-1"] } phf = { version = "0.11.2", features = ["macros"] } serde = { version = "1.0.195", features = ["derive"] } toml = "0.8.8" @@ -54,3 +54,6 @@ required-features = ["build-binary"] [[bin]] name = "shuasm" required-features = ["build-binary"] + +[workspace] +members = ["crates/control-flow-graph-wasm", "crates/ir-breadboard"] diff --git a/crates/ir-breadboard/Cargo.toml b/crates/ir-breadboard/Cargo.toml new file mode 100644 index 0000000..f7de815 --- /dev/null +++ b/crates/ir-breadboard/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ir-breadboard" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.203", features = ["derive"] } +serde-wasm-bindgen = "0.6.5" +wasm-bindgen = "0.2.92" +come = { path = "../.." } + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +opt-level = "z" +lto = true diff --git a/crates/ir-breadboard/src/lib.rs b/crates/ir-breadboard/src/lib.rs new file mode 100644 index 0000000..1ebbe3f --- /dev/null +++ b/crates/ir-breadboard/src/lib.rs @@ -0,0 +1,161 @@ +use std::str::FromStr; + +use come::ir::{ + self, + analyzer::{self, control_flow::structural::FoldedCFG, ControlFlowGraph, IsAnalyzer}, + optimize::{optimize as optimize_ir, pass::Pass}, + IR, +}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn parse(code: &str) -> JsValue { + let (_, parsed_ir) = ir::parse(code).unwrap(); + let result = parsed_ir.as_function_definition(); + serde_wasm_bindgen::to_value(&result).unwrap() +} + +#[wasm_bindgen] +pub fn optimize(code: &str, pass: &str) -> String { + let ir_code = ir::parse(code).unwrap().1; + let pass = Pass::from_str(pass).unwrap(); + let result = optimize_ir(vec![ir_code], vec![pass]) + .into_iter() + .next() + .unwrap(); + format!("{result}") +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct Edge { + pub from: u32, + pub to: u32, + pub back: bool, +} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Default, Debug)] +pub struct CFGraph { + pub nodes: Vec, + pub edges: Vec, +} + +#[wasm_bindgen] +pub fn dump_control_flow_graph(code: &str) -> CFGraph { + let ir_code = ir::parse(code).unwrap().1; + if let IR::FunctionDefinition(f) = ir_code { + let cfg = analyzer::ControlFlowGraph::new(); + let cfg = cfg.bind(&f); + let backedges = cfg.back_edges(); + let mut result = CFGraph::default(); + let g = cfg.graph(); + for n in g.node_indices() { + if n.index() == g.node_count() - 1 { + result.nodes.push("_dummy_end".to_string()); + } else { + result + .nodes + .push(cfg.basic_block_name_by_index(n.index()).to_string()); + } + } + for e in g.edge_indices() { + let (from, to) = g.edge_endpoints(e).unwrap(); + let is_backedge = backedges.contains(&(from.index() as _, to.index() as _)); + result.edges.push(Edge { + from: from.index() as _, + to: to.index() as _, + back: is_backedge, + }); + } + result + } else { + panic!("faq") + } +} + +#[wasm_bindgen] +pub fn structural_analysis(code: &str) -> JsValue { + let ir_code = ir::parse(code).unwrap().1; + let f = ir_code.as_function_definition(); + let cfg = ControlFlowGraph::new(); + let cfg = cfg.bind(f); + let folded = FoldedCFG::from_control_flow_graph(&cfg); + let result = folded.structural_analysis(&cfg); + serde_wasm_bindgen::to_value(&result).unwrap() +} + +#[test] +fn test_optimize() { + dbg!(optimize( + r"fn main() -> () { + %0 = add i32 1, 2 + ret + }", + "FixIrreducible" + )); +} + +#[test] +fn test_dump_cfg() { + dbg!(dump_control_flow_graph( + r"fn test_condition(i32 %a, i32 %b) -> i32 { + test_condition_entry: + %a_0_addr = alloca i32 + store i32 %a, address %a_0_addr + %b_0_addr = alloca i32 + store i32 %b, address %b_0_addr + %result_0_addr = alloca i32 + store i32 0, address %result_0_addr + %i_0_addr = alloca i32 + %0 = load i32 %a_0_addr + store i32 %0, address %i_0_addr + j loop_0_condition + loop_0_condition: + %2 = load i32 %i_0_addr + %3 = load i32 %b_0_addr + %1 = slt i32 %2, %3 + bne %1, 0, loop_0_success, loop_0_fail + loop_0_success: + %5 = load i32 %result_0_addr + %6 = load i32 %i_0_addr + %4 = add i32 %5, %6 + store i32 %4, address %result_0_addr + %8 = load i32 %i_0_addr + %7 = add i32 %8, 1 + store i32 %7, address %i_0_addr + j loop_0_condition + loop_0_fail: + %9 = load i32 %result_0_addr + ret %9 + }" + )); +} + +#[test] +fn test_structural_analysis() { + let code = r"fn test_condition(i32 %a, i32 %b) -> i32 { + test_condition_entry: + %a_0_addr = alloca i32 + store i32 %a, address %a_0_addr + %b_0_addr = alloca i32 + store i32 %b, address %b_0_addr + %1 = load i32 %a_0_addr + %2 = load i32 %b_0_addr + %0 = slt i32 %1, %2 + bne %0, 0, if_0_success, if_0_fail + if_0_success: + %3 = load i32 %a_0_addr + ret %3 + if_0_fail: + %4 = load i32 %b_0_addr + ret %4 + }"; + let ir_code = ir::parse(code).unwrap().1; + let f = ir_code.as_function_definition(); + let cfg = ControlFlowGraph::new(); + let cfg = cfg.bind(f); + let folded = FoldedCFG::from_control_flow_graph(&cfg); + let result = folded.structural_analysis(&cfg); + dbg!(result); +} diff --git a/doc/content/tools/ir_breadboard.md b/doc/content/tools/ir_breadboard.md new file mode 100644 index 0000000..185a66b --- /dev/null +++ b/doc/content/tools/ir_breadboard.md @@ -0,0 +1,409 @@ ++++ +title = "IR Breadboard" +description = "A tool for visualizing different information of given IR" +date = 2024-06-20T16:21:18.153Z +updated = 2024-06-20T16:21:18.153Z +draft = false ++++ + +
+ + +
+ + +
+
+ + + + + diff --git a/doc/static/tools/ir-breadboard/.gitignore b/doc/static/tools/ir-breadboard/.gitignore new file mode 100644 index 0000000..2d6d13a --- /dev/null +++ b/doc/static/tools/ir-breadboard/.gitignore @@ -0,0 +1,4 @@ +*.wasm +*.js +*.ts +package.json diff --git a/src/backend/riscv/simple_instruction/param_transformer/branch_high.rs b/src/backend/riscv/simple_instruction/param_transformer/branch_high.rs index 0d8d1c8..5168866 100644 --- a/src/backend/riscv/simple_instruction/param_transformer/branch_high.rs +++ b/src/backend/riscv/simple_instruction/param_transformer/branch_high.rs @@ -8,6 +8,12 @@ use bitvec::prelude::*; #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct BranchHigh; +impl Default for BranchHigh { + fn default() -> Self { + Self::new() + } +} + impl BranchHigh { pub const fn new() -> Self { Self diff --git a/src/backend/riscv/simple_instruction/param_transformer/branch_low.rs b/src/backend/riscv/simple_instruction/param_transformer/branch_low.rs index 2ab638d..4fee972 100644 --- a/src/backend/riscv/simple_instruction/param_transformer/branch_low.rs +++ b/src/backend/riscv/simple_instruction/param_transformer/branch_low.rs @@ -8,6 +8,12 @@ use bitvec::prelude::*; #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct BranchLow; +impl Default for BranchLow { + fn default() -> Self { + Self::new() + } +} + impl BranchLow { pub const fn new() -> Self { Self diff --git a/src/backend/riscv/simple_instruction/param_transformer/csr.rs b/src/backend/riscv/simple_instruction/param_transformer/csr.rs index b345555..5b177f1 100644 --- a/src/backend/riscv/simple_instruction/param_transformer/csr.rs +++ b/src/backend/riscv/simple_instruction/param_transformer/csr.rs @@ -8,6 +8,12 @@ use bitvec::prelude::*; #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Csr; +impl Default for Csr { + fn default() -> Self { + Self::new() + } +} + impl Csr { pub const fn new() -> Self { Self diff --git a/src/backend/riscv/simple_instruction/param_transformer/jal_form.rs b/src/backend/riscv/simple_instruction/param_transformer/jal_form.rs index 1f5f1a2..6a51a25 100644 --- a/src/backend/riscv/simple_instruction/param_transformer/jal_form.rs +++ b/src/backend/riscv/simple_instruction/param_transformer/jal_form.rs @@ -8,6 +8,12 @@ use super::IsParamTransformer; #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct JalForm; +impl Default for JalForm { + fn default() -> Self { + Self::new() + } +} + impl JalForm { pub const fn new() -> Self { Self diff --git a/src/backend/riscv/simple_instruction/param_transformer/register.rs b/src/backend/riscv/simple_instruction/param_transformer/register.rs index aa7e6d4..4f28023 100644 --- a/src/backend/riscv/simple_instruction/param_transformer/register.rs +++ b/src/backend/riscv/simple_instruction/param_transformer/register.rs @@ -8,6 +8,12 @@ use super::IsParamTransformer; #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct Register; +impl Default for Register { + fn default() -> Self { + Self::new() + } +} + impl Register { pub const fn new() -> Self { Self diff --git a/src/ir/editor/analyzer/control_flow/mod.rs b/src/ir/editor/analyzer/control_flow/mod.rs index acbf79a..229cf24 100644 --- a/src/ir/editor/analyzer/control_flow/mod.rs +++ b/src/ir/editor/analyzer/control_flow/mod.rs @@ -24,7 +24,7 @@ pub use self::scc::BindedScc; use super::IsAnalyzer; mod scc; -mod structural; +pub mod structural; /// [`ControlFlowGraph`] is the control flow graph and related infomation of a function. #[derive(Debug)] @@ -294,7 +294,7 @@ impl<'item, 'bind: 'item> BindedControlFlowGraph<'item, 'bind> { ) } pub fn back_edges(&self) -> Vec<(usize, usize)> { - self.item.back_edges(&self.bind_on) + self.item.back_edges(self.bind_on) } } diff --git a/src/ir/editor/analyzer/control_flow/structural/mod.rs b/src/ir/editor/analyzer/control_flow/structural/mod.rs index b18c62e..723c505 100644 --- a/src/ir/editor/analyzer/control_flow/structural/mod.rs +++ b/src/ir/editor/analyzer/control_flow/structural/mod.rs @@ -8,13 +8,14 @@ use petgraph::{ visit::{depth_first_search, DfsEvent, DfsPostOrder, EdgeRef}, Direction, }; +use serde::{Deserialize, Serialize}; use super::BindedControlFlowGraph; mod selector; -#[derive(Clone, Debug, Default)] -struct FoldedCFG { +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct FoldedCFG { graph: StableDiGraph, usize>, entry: NodeIndex, } @@ -45,14 +46,14 @@ fn back_edges(c: &FoldedCFG) -> Vec<(NodeIndex, NodeIndex)> { result } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] struct If { content: FoldedCFG, on_then: NodeIndex, on_else: Option>, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] enum RegionNode { Single(usize), Loop(FoldedCFG), @@ -143,7 +144,7 @@ impl FoldedCFG { let mut loop_subgraph = FoldedCFG::default(); let mut node_index_map = HashMap::new(); // id in old => id in new for loop_content_index in &smallest_loop_content { - let loop_content_node = &content.graph[loop_content_index.clone()]; + let loop_content_node = &content.graph[*loop_content_index]; node_index_map.insert( loop_content_index, loop_subgraph.graph.add_node(loop_content_node.clone()), @@ -155,16 +156,15 @@ impl FoldedCFG { for loop_content_index in &smallest_loop_content { let incoming_edges = content .graph - .edges_directed(loop_content_index.clone(), Direction::Incoming) - .into_iter(); + .edges_directed(*loop_content_index, Direction::Incoming); let (intern_edges, extern_edges) = incoming_edges .partition::, _>(|it| node_index_map.contains_key(&it.source())); for intern_edge in intern_edges { let intern_from = node_index_map.get(&intern_edge.source()).unwrap(); let intern_target = node_index_map.get(&intern_edge.target()).unwrap(); loop_subgraph.graph.add_edge( - intern_from.clone(), - intern_target.clone(), + *intern_from, + *intern_target, intern_edge.weight().clone(), ); } @@ -177,8 +177,7 @@ impl FoldedCFG { let outgoing_edges = content .graph - .edges_directed(loop_content_index.clone(), Direction::Outgoing) - .into_iter(); + .edges_directed(*loop_content_index, Direction::Outgoing); let (_intern_edges, extern_edges) = outgoing_edges .partition::, _>(|it| node_index_map.contains_key(&it.target())); // no need to handle `intern_edges`, because they will be handled of `incoming_edges` on the other side @@ -228,14 +227,13 @@ impl FoldedCFG { } pub fn from_control_flow_graph(graph: &BindedControlFlowGraph) -> FoldedCFG { let graph_ = graph.graph(); - let mut result = graph_.map( + let result = graph_.map( |node_index, _| RegionNode::Single(node_index.index()), |edge_id, _| { let (from, to) = graph_.edge_endpoints(edge_id).unwrap(); vec![(from.index(), to.index())] }, ); - result.remove_node(graph.bind_on.content.len().into()); FoldedCFG { graph: result.into(), entry: 0.into(), @@ -250,13 +248,6 @@ fn fold_if_else( ) -> bool { let (outgoing_node_a, outgoing_node_b) = outgoing_nodes_from_condition(region_content, condition, cfg); - println!( - "fold_if_else on {}, cond={}, a_side={}, b_side={}", - region_content, - region_content.graph[condition], - region_content.graph[outgoing_node_a], - region_content.graph[outgoing_node_b], - ); let subregion_node = ®ion_content.graph[condition]; let subregion_a = ®ion_content.graph[outgoing_node_a].clone(); let mut new_region_content = FoldedCFG { @@ -269,20 +260,10 @@ fn fold_if_else( .graph .neighbors_directed(outgoing_node_a, Direction::Outgoing) .collect_vec(); - print!("after_a: "); - for aa in &after_a { - print!("{}, ", region_content.graph[aa.clone()]); - } - println!(); let after_b = region_content .graph .neighbors_directed(outgoing_node_b, Direction::Outgoing) .collect_vec(); - print!("after_b: "); - for bb in &after_b { - print!("{}, ", region_content.graph[bb.clone()]); - } - println!(); let edges_into_node = region_content .graph .edges_directed(condition, Direction::Incoming) @@ -290,9 +271,17 @@ fn fold_if_else( .collect_vec(); if after_a.contains(&outgoing_node_b) { // if, without an else - println!("if, without an else"); // the content should only contains the condition (`node`) and the then part (`a`) let inserted_subregion_a_index = new_region_content.graph.add_node(subregion_a.clone()); + let edge_condition_to_a = region_content + .graph + .find_edge(condition, outgoing_node_a) + .unwrap(); + new_region_content.graph.add_edge( + inserted_subregion_node_index, + inserted_subregion_a_index, + region_content.graph[edge_condition_to_a].clone(), + ); let new_if = If { content: new_region_content, on_then: inserted_subregion_a_index, @@ -320,7 +309,7 @@ fn fold_if_else( let _new_edge = region_content.graph.add_edge( new_node, outgoing_node_b, - Iterator::chain(condition_to_b.into_iter(), a_to_b.into_iter()) + Iterator::chain(condition_to_b.iter(), a_to_b) .cloned() .collect(), ); @@ -332,21 +321,26 @@ fn fold_if_else( } else if after_a.len() == 1 && after_a == after_b { let c = after_a[0]; // if, with an else - println!("if, with an else"); let subregion_b = ®ion_content.graph[outgoing_node_b].clone(); let inserted_subregion_a_index = new_region_content.graph.add_node(subregion_a.clone()); let inserted_subregion_b_index = new_region_content.graph.add_node(subregion_b.clone()); - println!( - "{}: subregion_a={} == {}", - inserted_subregion_a_index.index(), - subregion_a, - &new_region_content.graph[inserted_subregion_a_index], + let edge_condition_to_a = region_content + .graph + .find_edge(condition, outgoing_node_a) + .unwrap(); + let edge_condition_to_b = region_content + .graph + .find_edge(condition, outgoing_node_b) + .unwrap(); + new_region_content.graph.add_edge( + inserted_subregion_node_index, + inserted_subregion_a_index, + region_content.graph[edge_condition_to_a].clone(), ); - println!( - "{}: subregion_b={} == {}", - inserted_subregion_b_index.index(), - subregion_b, - &new_region_content.graph[inserted_subregion_b_index], + new_region_content.graph.add_edge( + inserted_subregion_node_index, + inserted_subregion_b_index, + region_content.graph[edge_condition_to_b].clone(), ); let new_if = If { content: new_region_content, @@ -372,7 +366,7 @@ fn fold_if_else( ) .collect_tuple() .unwrap(); - let new_weight = Iterator::chain(a_to_c.weight().into_iter(), b_to_c.weight().into_iter()) + let new_weight = Iterator::chain(a_to_c.weight().iter(), b_to_c.weight()) .cloned() .collect(); let _new_edge = region_content.graph.add_edge(new_node, c, new_weight); @@ -436,19 +430,19 @@ fn single_entry_single_exit_nodes( // detect up while content .graph - .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .neighbors_directed(current_looking_at_node, Direction::Incoming) .count() == 1 && content .graph - .neighbors_directed(current_looking_at_node.into(), Direction::Outgoing) + .neighbors_directed(current_looking_at_node, Direction::Outgoing) .count() == 1 { result.push(current_looking_at_node); current_looking_at_node = content .graph - .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .neighbors_directed(current_looking_at_node, Direction::Incoming) .exactly_one() .unwrap(); } @@ -458,12 +452,12 @@ fn single_entry_single_exit_nodes( // detect down while content .graph - .neighbors_directed(current_looking_at_node.into(), Direction::Incoming) + .neighbors_directed(current_looking_at_node, Direction::Incoming) .count() == 1 && content .graph - .neighbors_directed(current_looking_at_node.into(), Direction::Outgoing) + .neighbors_directed(current_looking_at_node, Direction::Outgoing) .count() == 1 { @@ -742,4 +736,34 @@ mod tests { let saed = FoldedCFG::structural_analysis(folded_cfg, &binded); println!("{}", saed); } + + #[test] + fn test_structual_analysis4() { + let code = r"fn test_condition(i32 %a, i32 %b) -> i32 { + test_condition_entry: + %a_0_addr = alloca i32 + store i32 %a, address %a_0_addr + %b_0_addr = alloca i32 + store i32 %b, address %b_0_addr + %1 = load i32 %a_0_addr + %2 = load i32 %b_0_addr + %0 = slt i32 %1, %2 + bne %0, 0, if_0_success, if_0_fail + if_0_success: + %3 = load i32 %a_0_addr + ret %3 + if_0_fail: + %4 = load i32 %b_0_addr + ret %4 + if_0_end: + ret + }"; + let ir_code = ir::parse(code).unwrap().1; + let f = ir_code.as_function_definition(); + let cfg = ControlFlowGraph::new(); + let cfg = cfg.bind(f); + let folded = FoldedCFG::from_control_flow_graph(&cfg); + let result = folded.structural_analysis(&cfg); + dbg!(result); + } } diff --git a/src/ir/editor/analyzer/mod.rs b/src/ir/editor/analyzer/mod.rs index 0b3b0d8..ef91459 100644 --- a/src/ir/editor/analyzer/mod.rs +++ b/src/ir/editor/analyzer/mod.rs @@ -8,7 +8,7 @@ pub use self::{ }; use super::action::Action; -mod control_flow; +pub mod control_flow; mod memory_usage; pub mod register_usage; diff --git a/src/ir/function/basic_block.rs b/src/ir/function/basic_block.rs index e95c009..ec6923e 100644 --- a/src/ir/function/basic_block.rs +++ b/src/ir/function/basic_block.rs @@ -1,3 +1,7 @@ +use super::{ + statement::{self, IRStatement}, + IsIRStatement, +}; use crate::{ ir::RegisterName, utility::{data_type::Type, parsing}, @@ -11,15 +15,11 @@ use nom::{ sequence::{pair, tuple}, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; -use super::{ - statement::{self, IRStatement}, - IsIRStatement, -}; - /// A basic block. -#[derive(Debug, Eq, PartialEq, Clone, Hash, Default)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Default, Serialize, Deserialize)] pub struct BasicBlock { /// Name of the basic block. pub name: Option, diff --git a/src/ir/function/mod.rs b/src/ir/function/mod.rs index 88d874d..b73b8c9 100644 --- a/src/ir/function/mod.rs +++ b/src/ir/function/mod.rs @@ -15,6 +15,7 @@ use nom::{ IResult, }; use parameter::Parameter; +use serde::{Deserialize, Serialize}; use statement::*; use std::{ fmt, mem, @@ -106,7 +107,7 @@ impl<'a> Iter<'a> { } } -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct FunctionHeader { /// Name of the function. pub name: String, @@ -117,7 +118,7 @@ pub struct FunctionHeader { } /// [`FunctionDefinition`] represents a function definition. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct FunctionDefinition { pub header: FunctionHeader, /// Basic blocks of the function. @@ -292,3 +293,16 @@ pub fn formalize(mut function: FunctionDefinition) -> FunctionDefinition { } function } + +#[cfg(test)] +mod tests { + use super::*; + // todo: more tests + #[test] + fn test_parse() { + let code = r"fn main() -> () { + %0 = add i32 1, 2 + }"; + assert!(parse(code).is_ok()); + } +} diff --git a/src/ir/function/parameter.rs b/src/ir/function/parameter.rs index b5fcd93..03e663c 100644 --- a/src/ir/function/parameter.rs +++ b/src/ir/function/parameter.rs @@ -5,9 +5,9 @@ use crate::{ ir::{quantity::local, RegisterName}, utility::data_type::{self, Type}, }; - +use serde::{Deserialize, Serialize}; /// [`Parameter`] represents a function's parameter. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct Parameter { /// Name of the parameter. pub name: RegisterName, diff --git a/src/ir/function/statement/alloca.rs b/src/ir/function/statement/alloca.rs index 556d8e5..fe47999 100644 --- a/src/ir/function/statement/alloca.rs +++ b/src/ir/function/statement/alloca.rs @@ -12,10 +12,10 @@ use nom::{ sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; - /// [`Alloca`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Alloca { /// Local variable, pointing to the space allocated on the stack. pub to: RegisterName, diff --git a/src/ir/function/statement/branch.rs b/src/ir/function/statement/branch.rs index 74fde31..7cfd77c 100644 --- a/src/ir/function/statement/branch.rs +++ b/src/ir/function/statement/branch.rs @@ -1,3 +1,4 @@ +use super::calculate::{binary::BinaryOperation, BinaryCalculate}; use crate::{ ir::{ function::IsIRStatement, @@ -17,15 +18,14 @@ use nom::{ sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; use std::{ fmt, fmt::{Display, Formatter}, }; -use super::calculate::{binary::BinaryOperation, BinaryCalculate}; - /// Enum of all possible branch types. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum BranchType { EQ, NE, @@ -49,7 +49,7 @@ fn branch_type(code: &str) -> IResult<&str, BranchType> { } /// [`Branch`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] pub struct Branch { /// Type of the branch. pub branch_type: BranchType, diff --git a/src/ir/function/statement/calculate/binary.rs b/src/ir/function/statement/calculate/binary.rs index 53b1ae4..34a1a80 100644 --- a/src/ir/function/statement/calculate/binary.rs +++ b/src/ir/function/statement/calculate/binary.rs @@ -19,10 +19,10 @@ use nom::{ IResult, }; use phf::phf_map; +use serde::{Deserialize, Serialize}; use std::fmt; - /// [`BinaryOperation`] represents a binary operation operator. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum BinaryOperation { Add, LessThan, @@ -104,7 +104,7 @@ fn binary_operation(code: &str) -> IResult<&str, BinaryOperation> { } /// [`BinaryCalculate`] represents a binary operation statement. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] pub struct BinaryCalculate { pub operation: BinaryOperation, pub operand1: Quantity, diff --git a/src/ir/function/statement/calculate/unary.rs b/src/ir/function/statement/calculate/unary.rs index 90e5c7a..d839c62 100644 --- a/src/ir/function/statement/calculate/unary.rs +++ b/src/ir/function/statement/calculate/unary.rs @@ -19,10 +19,10 @@ use nom::{ IResult, }; use phf::phf_map; +use serde::{Deserialize, Serialize}; use std::fmt; - /// [`UnaryOperation`] represents a unary operation operator. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum UnaryOperation { Neg, Not, @@ -51,7 +51,7 @@ impl fmt::Display for UnaryOperation { } /// [`UnaryCalculate`] represents the result of a unary operator. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] pub struct UnaryCalculate { pub operation: UnaryOperation, pub operand: Quantity, diff --git a/src/ir/function/statement/call.rs b/src/ir/function/statement/call.rs index 38ddd6a..474f309 100644 --- a/src/ir/function/statement/call.rs +++ b/src/ir/function/statement/call.rs @@ -19,10 +19,10 @@ use nom::{ sequence::{delimited, tuple}, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; - /// [`Call`] instruction. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct Call { /// Where to store the result of the call. pub to: Option, diff --git a/src/ir/function/statement/jump.rs b/src/ir/function/statement/jump.rs index b8986cb..74b4d1a 100644 --- a/src/ir/function/statement/jump.rs +++ b/src/ir/function/statement/jump.rs @@ -5,13 +5,13 @@ use crate::{ use nom::{ bytes::complete::tag, character::complete::space1, combinator::map, sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; use std::{ fmt, fmt::{Display, Formatter}, }; - /// [`Jump`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Jump { pub label: String, } diff --git a/src/ir/function/statement/load.rs b/src/ir/function/statement/load.rs index 740700a..ed38a3a 100644 --- a/src/ir/function/statement/load.rs +++ b/src/ir/function/statement/load.rs @@ -14,9 +14,9 @@ use nom::{ sequence::tuple, IResult, }; - +use serde::{Deserialize, Serialize}; /// [`Load`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Load { pub to: RegisterName, pub data_type: Type, diff --git a/src/ir/function/statement/load_field.rs b/src/ir/function/statement/load_field.rs index fd2103e..05be7de 100644 --- a/src/ir/function/statement/load_field.rs +++ b/src/ir/function/statement/load_field.rs @@ -1,3 +1,4 @@ +use super::Load; use crate::{ ast::{ self, @@ -21,12 +22,11 @@ use nom::{ sequence::{delimited, tuple}, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; -use super::Load; - /// [`LoadField`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct LoadField { /// Where to store the result of the load. pub target: RegisterName, diff --git a/src/ir/function/statement/mod.rs b/src/ir/function/statement/mod.rs index 4e88fef..f6c50d3 100644 --- a/src/ir/function/statement/mod.rs +++ b/src/ir/function/statement/mod.rs @@ -3,6 +3,7 @@ use std::fmt; use enum_dispatch::enum_dispatch; use nom::{branch::alt, combinator::map, IResult}; use paste::paste; +use serde::{Deserialize, Serialize}; /// Data structure, parser and ir generator for `alloca` statement. mod alloca; @@ -27,6 +28,10 @@ pub(crate) mod set_field; /// Data structure, parser and ir generator for `store` statement. mod store; +use crate::{ + ir::{quantity::Quantity, RegisterName}, + utility::data_type::Type, +}; pub use alloca::Alloca; pub use branch::Branch; pub use calculate::{BinaryCalculate, UnaryCalculate}; @@ -39,11 +44,6 @@ pub use ret::Ret; pub use set_field::SetField; pub use store::Store; -use crate::{ - ir::{quantity::Quantity, RegisterName}, - utility::data_type::Type, -}; - /// This trait should be implemented for all IRStatements #[enum_dispatch] pub trait IsIRStatement { @@ -54,7 +54,7 @@ pub trait IsIRStatement { /// A statement in a function. #[enum_dispatch(IsIRStatement)] -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub enum IRStatement { Phi, Alloca, diff --git a/src/ir/function/statement/phi.rs b/src/ir/function/statement/phi.rs index e9a4aaa..7f17592 100644 --- a/src/ir/function/statement/phi.rs +++ b/src/ir/function/statement/phi.rs @@ -17,10 +17,10 @@ use nom::{ sequence::{delimited, tuple}, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; - /// [`Phi`]'s source. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] pub struct PhiSource { pub value: Quantity, pub block: String, @@ -50,7 +50,7 @@ fn parse_phi_source(code: &str) -> IResult<&str, PhiSource> { } /// [`Phi`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Phi { /// Where to store the result of the phi. pub to: RegisterName, diff --git a/src/ir/function/statement/ret.rs b/src/ir/function/statement/ret.rs index 1196e92..095074a 100644 --- a/src/ir/function/statement/ret.rs +++ b/src/ir/function/statement/ret.rs @@ -13,10 +13,10 @@ use nom::{ sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; - /// [`Ret`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Ret { pub value: Option, } diff --git a/src/ir/function/statement/set_field.rs b/src/ir/function/statement/set_field.rs index 9d81b7c..175deb8 100644 --- a/src/ir/function/statement/set_field.rs +++ b/src/ir/function/statement/set_field.rs @@ -20,9 +20,9 @@ use nom::{ sequence::{delimited, tuple}, IResult, }; - +use serde::{Deserialize, Serialize}; /// [`SetField`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct SetField { /// Where to store the result. pub target: RegisterName, diff --git a/src/ir/function/statement/store.rs b/src/ir/function/statement/store.rs index e609555..be775fb 100644 --- a/src/ir/function/statement/store.rs +++ b/src/ir/function/statement/store.rs @@ -13,10 +13,10 @@ use nom::{ sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; - /// [`Store`] instruction. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Store { /// Type of the value to store. pub data_type: Type, diff --git a/src/ir/global_definition.rs b/src/ir/global_definition.rs index 2d7eb33..e64b5b9 100644 --- a/src/ir/global_definition.rs +++ b/src/ir/global_definition.rs @@ -17,9 +17,10 @@ use nom::{ sequence::tuple, IResult, }; +use serde::{Deserialize, Serialize}; /// [`GlobalDefinition`] represents a global variable definition. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct GlobalDefinition { /// Name of the global variable. pub name: GlobalVariableName, diff --git a/src/ir/integer_literal.rs b/src/ir/integer_literal.rs index b8f297b..9fecdc9 100644 --- a/src/ir/integer_literal.rs +++ b/src/ir/integer_literal.rs @@ -2,9 +2,10 @@ use std::fmt; use crate::{ast, utility::parsing}; use nom::{combinator::map, IResult}; +use serde::{Deserialize, Serialize}; /// An integer literal. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct IntegerLiteral(pub i64); impl From for IntegerLiteral { diff --git a/src/ir/mod.rs b/src/ir/mod.rs index f30f5c2..24b7541 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -4,6 +4,7 @@ use std::{ }; use enum_dispatch::enum_dispatch; +use serde::{Deserialize, Serialize}; /// Data structure, parser and ir generator for functions. pub mod function; @@ -35,13 +36,23 @@ pub use editor::analyzer; /// The root nodes of IR. #[enum_dispatch] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum IR { TypeDefinition, FunctionDefinition, GlobalDefinition, } +impl IR { + pub fn as_function_definition(&self) -> &FunctionDefinition { + if let IR::FunctionDefinition(f) = self { + f + } else { + panic!("This is not a function definition!") + } + } +} + impl fmt::Display for IR { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -167,4 +178,15 @@ pub fn from_ast(ast: &Ast) -> Vec { .collect() } -// todo: tests +#[cfg(test)] +mod tests { + use super::*; + // todo: more tests + #[test] + fn test_parse() { + let code = r"fn main() -> () { + %0 = add i32 1, 2 + }"; + assert!(parse(code).is_ok()); + } +} diff --git a/src/ir/optimize/pass/fix_irreducible/mod.rs b/src/ir/optimize/pass/fix_irreducible/mod.rs index 7a58eef..53e3d8c 100644 --- a/src/ir/optimize/pass/fix_irreducible/mod.rs +++ b/src/ir/optimize/pass/fix_irreducible/mod.rs @@ -1,10 +1,10 @@ +use itertools::Itertools; +use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeSet, HashMap, HashSet}, hash::Hash, }; -use itertools::Itertools; - use crate::{ ir::{ analyzer::BindedControlFlowGraph, @@ -20,7 +20,7 @@ use crate::{ use super::{IsPass, Pass}; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct FixIrreducible; #[derive(Debug, Hash, PartialEq, Eq, Clone)] @@ -184,7 +184,7 @@ fn generate_edit_plan( // Generate all the branches, include the last one let mut branches = phis .iter() - .zip(origin_targets.into_iter()) + .zip(origin_targets) .tuple_windows() .map( |((depend_on_phi_result, target_block_index), (_, next_target_block_index))| Branch { @@ -287,7 +287,7 @@ fn execute_edit_plan(function: &mut FunctionDefinition, plan: EditPlan) { for fix_plan in plan.fix_other_block_plan { fix_other_block(function, &first_guard_block_name, &plan.scc_id, fix_plan); } - function.content.extend(guard_blocks.into_iter()); + function.content.extend(guard_blocks); } fn generate_origin_target_to_source_map( diff --git a/src/ir/optimize/pass/memory_to_register.rs b/src/ir/optimize/pass/memory_to_register.rs index 05babae..7e831fe 100644 --- a/src/ir/optimize/pass/memory_to_register.rs +++ b/src/ir/optimize/pass/memory_to_register.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use itertools::Itertools; - use crate::{ ir::{ editor::{analyzer, Editor}, @@ -11,6 +9,8 @@ use crate::{ }, utility::data_type::Type, }; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; use super::{ remove_only_once_store::RemoveOnlyOnceStore, remove_unused_register::RemoveUnusedRegister, @@ -19,7 +19,7 @@ use super::{ /// [`MemoryToRegister`] is a pass that convert memory access to register access. /// It is similar to LLVM's [`mem2reg`](https://llvm.org/docs/Passes.html#mem2reg-promote-memory-to-register). -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct MemoryToRegister; impl IsPass for MemoryToRegister { diff --git a/src/ir/optimize/pass/mod.rs b/src/ir/optimize/pass/mod.rs index 533dfe2..c1eb594 100644 --- a/src/ir/optimize/pass/mod.rs +++ b/src/ir/optimize/pass/mod.rs @@ -11,9 +11,9 @@ use memory_to_register::MemoryToRegister; use remove_load_directly_after_store::RemoveLoadDirectlyAfterStore; use remove_only_once_store::RemoveOnlyOnceStore; use remove_unused_register::RemoveUnusedRegister; -pub use topological_sort::TopologicalSort; - +use serde::{Deserialize, Serialize}; 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 { @@ -28,7 +28,7 @@ pub trait IsPass { /// All passes which can do optimizing on ir function. #[enum_dispatch(IsPass)] -#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub enum Pass { RemoveUnusedRegister, RemoveOnlyOnceStore, diff --git a/src/ir/optimize/pass/remove_load_directly_after_store.rs b/src/ir/optimize/pass/remove_load_directly_after_store.rs index 45b22b1..ee77eb1 100644 --- a/src/ir/optimize/pass/remove_load_directly_after_store.rs +++ b/src/ir/optimize/pass/remove_load_directly_after_store.rs @@ -1,11 +1,11 @@ -use crate::ir::editor::Editor; - use super::IsPass; +use crate::ir::editor::Editor; +use serde::{Deserialize, Serialize}; /// This pass will remove all load instructions which are /// - in same block with a store instruction /// - after the store instruction. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct RemoveLoadDirectlyAfterStore; impl IsPass for RemoveLoadDirectlyAfterStore { diff --git a/src/ir/optimize/pass/remove_only_once_store.rs b/src/ir/optimize/pass/remove_only_once_store.rs index bf26a78..edc0206 100644 --- a/src/ir/optimize/pass/remove_only_once_store.rs +++ b/src/ir/optimize/pass/remove_only_once_store.rs @@ -1,10 +1,10 @@ use super::IsPass; - +use serde::{Deserialize, Serialize}; /// This pass will /// - remove all store statements which is the only one store to a variable /// - remove the load statements to the variable /// - replace all usage of the load results to the source of the store -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct RemoveOnlyOnceStore; impl IsPass for RemoveOnlyOnceStore { diff --git a/src/ir/optimize/pass/remove_unused_register.rs b/src/ir/optimize/pass/remove_unused_register.rs index 1fad791..e11d9ba 100644 --- a/src/ir/optimize/pass/remove_unused_register.rs +++ b/src/ir/optimize/pass/remove_unused_register.rs @@ -1,9 +1,8 @@ -use crate::ir::editor; - use super::IsPass; -use crate::ir::editor::analyzer::register_usage::RegisterDefinePosition; +use crate::ir::{editor, editor::analyzer::register_usage::RegisterDefinePosition}; +use serde::{Deserialize, Serialize}; /// This pass will remove the register which are defined but not used. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct RemoveUnusedRegister; impl IsPass for RemoveUnusedRegister { diff --git a/src/ir/optimize/pass/topological_sort.rs b/src/ir/optimize/pass/topological_sort.rs index e0208f9..32d524a 100644 --- a/src/ir/optimize/pass/topological_sort.rs +++ b/src/ir/optimize/pass/topological_sort.rs @@ -1,7 +1,7 @@ -use std::mem; - use itertools::Itertools; use petgraph::prelude::*; +use serde::{Deserialize, Serialize}; +use std::mem; use crate::ir::{ analyzer::{BindedControlFlowGraph, IsAnalyzer}, @@ -10,7 +10,7 @@ use crate::ir::{ use super::IsPass; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct TopologicalSort; impl IsPass for TopologicalSort { diff --git a/src/ir/quantity/global.rs b/src/ir/quantity/global.rs index 27f59d8..35e6412 100644 --- a/src/ir/quantity/global.rs +++ b/src/ir/quantity/global.rs @@ -1,9 +1,10 @@ use crate::utility::parsing; use nom::{bytes::complete::tag, combinator::map, sequence::pair, IResult}; +use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; /// [`GlobalVariableName`] represents a global variable's name. -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct GlobalVariableName(pub String); impl Display for GlobalVariableName { diff --git a/src/ir/quantity/local.rs b/src/ir/quantity/local.rs index f3690d4..6ae86b4 100644 --- a/src/ir/quantity/local.rs +++ b/src/ir/quantity/local.rs @@ -7,10 +7,11 @@ use nom::{ sequence::pair, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; /// [`RegisterName`] represents a local variable's name. -#[derive(Debug, Eq, PartialEq, Clone, Hash, PartialOrd, Ord)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct RegisterName(pub String); impl Display for RegisterName { diff --git a/src/ir/quantity/mod.rs b/src/ir/quantity/mod.rs index d87cd52..8cc075f 100644 --- a/src/ir/quantity/mod.rs +++ b/src/ir/quantity/mod.rs @@ -5,15 +5,15 @@ pub use crate::ir::quantity::{global::GlobalVariableName, local::RegisterName}; use crate::utility::parsing; use enum_dispatch::enum_dispatch; use nom::{branch::alt, combinator::map, IResult}; +use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; - /// Tag trait for [`Quantity`]. #[enum_dispatch] trait IsQuantity {} /// [`Quantity`] represents a variable (global or local) or a constant value #[enum_dispatch(IsQuantity)] -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)] pub enum Quantity { RegisterName, GlobalVariableName, diff --git a/src/ir/type_definition.rs b/src/ir/type_definition.rs index 80359f5..301c55b 100644 --- a/src/ir/type_definition.rs +++ b/src/ir/type_definition.rs @@ -11,10 +11,11 @@ use nom::{ sequence::{delimited, tuple}, IResult, }; +use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt}; /// [`TypeDefinition`] represents definition of a struct. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct TypeDefinition { pub name: String, pub fields: Vec, diff --git a/src/utility/data_type.rs b/src/utility/data_type.rs index 509c6ba..bd8b792 100644 --- a/src/utility/data_type.rs +++ b/src/utility/data_type.rs @@ -7,10 +7,11 @@ use nom::{ sequence::pair, IResult, }; +use serde::{Deserialize, Serialize}; use std::fmt; /// An integer type -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub struct Integer { /// Whether the integer is signed. pub signed: bool, @@ -19,7 +20,7 @@ pub struct Integer { } /// Type in IR -#[derive(Debug, Eq, PartialEq, Clone, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Hash, Serialize, Deserialize)] pub enum Type { Integer(Integer), StructRef(String),