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<String>, + pub edges: Vec<Edge>, +} + +#[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 ++++ + +<div id="container"> + <textarea id="code-input"></textarea> + <div id="menu"> + <button id="dump-cfg">Dump CFG</button> + <select name="optimize" id="optimize"> + <option value="MemoryToRegister">MemoryToRegister</option> + <option value="RemoveLoadDirectlyAfterStore">RemoveLoadDirectlyAfterStore</option> + <option value="RemoveOnlyOnceStore">RemoveOnlyOnceStore</option> + <option value="RemoveUnusedRegister">RemoveUnusedRegister</option> + <option value="TopologicalSort">TopologicalSort</option> + </select> + <button id="structural-analysis">Structural Analysis</button> + </div> + <div id="info"> + <pre class="hidden" id="output"></pre> + <svg class="hidden" id="svg-graph"> + </svg> + </div> +</div> + +<style> +.hidden { + display: none; +} +#container { + display: flex; + align-items: center; + margin-left: -24px; + margin-right: -24px; +} +#code-input { + width: 40%; + min-height: 100px; + height: 800px; + font-size: 12px; + font-family: 'Zed Mono', 'Fira Code', 'Source Code Pro', 'Courier New', Courier, monospace; +} +#menu { + width: 18%; + min-height: 100px; + font-size: 10pt; +} +#menu > * { + width: 100%; +} +#info { + width: 40%; + height: 800px; + overflow: scroll; +} +#svg-graph { + background: white; +} +#svg-graph text { + font-size: 10px; + stroke: 0; + font: 'Zed Mono', 'Fira Code', 'Source Code Pro', 'Courier New', Courier, monospace; + fill: #000; +} +.clickable { + cursor: pointer; +} +</style> + +<script src="https://dagrejs.github.io/project/dagre/latest/dagre.min.js"> +</script> +<script type="module"> +import init, { parse, optimize, dump_control_flow_graph, structural_analysis } from "./ir_breadboard.js"; + +const svgNS = "http://www.w3.org/2000/svg"; +const nodeRadius = 30; +let expanded = [[0]]; + +function fill_holes(graph) { + let new_nodes = []; + const length = graph.node_holes.length + graph.nodes.length; + for (let i = 0; i < length; ++i) { + if (graph.node_holes[0] === i) { + graph.node_holes.shift(); + new_nodes.push(undefined); + } else { + new_nodes.push(graph.nodes.shift()); + } + } + graph.nodes = new_nodes; +} + +function startsWith(arr, sequence) { + if (arr.length < sequence.length) { + return false; + } + for (let i = 0; i < sequence.length; i++) { + if (arr[i] !== sequence[i]) { + return false; + } + } + return true; +} + +function layoutAll(root_graph, path) { + fill_holes(root_graph); + let g = new dagre.graphlib.Graph(); + g.setGraph({marginx: nodeRadius, marginy: nodeRadius}); + g.setDefaultEdgeLabel(function() { return {}; }); + root_graph.nodes.forEach((node, index) => { + path.push(index); + + if (node !== undefined + && expanded.find(it => startsWith(it, path)) === undefined + && node.Single === undefined) { + // eg. + // expanded: [[1, 0, 1, 0], [2, 2, 2]] + // path: [1, 0, 1]: expanded, will not reach here + // path: [2, 0] : not expanded, reach heres + g.setNode(index, { + label: node, + width: nodeRadius * 4, + height: nodeRadius * 3 + }); + } else if (node !== undefined && node.Single !== undefined) { + g.setNode(index, { + label: node, + width: nodeRadius * 2, + height: nodeRadius * 2 + }); + } else if (node !== undefined && node.If !== undefined) { + layoutAll(node.If.content.graph, path); + g.setNode(index, { + label: node, + width: node.If.content.graph.layouted.graph().width, + height: node.If.content.graph.layouted.graph().height + }); + } else if (node !== undefined && node.Loop !== undefined) { + layoutAll(node.Loop.graph, path); + g.setNode(index, { + label: node, + width: node.Loop.graph.layouted.graph().width, + height: node.Loop.graph.layouted.graph().height + }); + } else if (node !== undefined && node.Block !== undefined) { + layoutAll(node.Block.graph, path); + g.setNode(index, { + label: node, + width: node.Block.graph.layouted.graph().width, + height: node.Block.graph.layouted.graph().height + }); + } else if (node !== undefined) { + console.error("unreachable"); + } + path.pop(); + }); + root_graph.edges.forEach(edge => { + if (edge) { + g.setEdge(edge[0], edge[1]); + } + }); + dagre.layout(g); + root_graph.layouted = g; + root_graph.path = path; +} + +function renderExpandableGraph(element, subgraph, path) { + subgraph.layouted.edges().forEach(function(e) { + let edge = subgraph.layouted.edge(e); + let points = edge.points; + const path = document.createElementNS(svgNS, "path"); + path.setAttribute("d", `M ${points[0].x} ${points[0].y} Q${points[1].x} ${points[1].y} ${points[2].x} ${points[2].y}`); + path.setAttribute("stroke", "black"); + path.setAttribute("fill", "transparent"); + path.setAttribute("style", "stroke:#000; marker-end: url(#arrow);"); + path.setAttribute("stroke-width", "1.5"); + let edge_info = subgraph.edges.find(it => it && it.from === parseInt(e.v) && it.to === parseInt(e.w)); + if (edge_info && edge_info.back) { + path.setAttribute('stroke-dasharray', '1.5 1.5'); + } + element.appendChild(path); + }); + subgraph.layouted.nodes().forEach((node, index) => { + path.push(parseInt(node)); + let node_object = subgraph.nodes[node]; + let node_layout = subgraph.layouted.node(node); + let node_subgraph = null; + if (node_object.If !== undefined) { + node_subgraph = node_object.If.content.graph; + } else if (node_object.Loop !== undefined) { + node_subgraph = node_object.Loop.graph; + } else if (node_object.Block !== undefined) { + node_subgraph = node_object.Block.graph; + } + if (node_object.Single !== undefined) { + const circle = document.createElementNS(svgNS, "circle"); + circle.setAttribute("r", nodeRadius); + circle.setAttribute("fill", "red"); + circle.setAttribute("cx", node_layout.x); + circle.setAttribute("cy", node_layout.y); + element.appendChild(circle); + } else if (node_subgraph !== null && node_subgraph.layouted === undefined) { + const group = document.createElementNS(svgNS, "g"); + group.setAttribute("transform", `translate(${node_layout.x - nodeRadius * 2}, ${node_layout.y - nodeRadius * 1.5})`); + + const rect = document.createElementNS(svgNS, "rect"); + rect.setAttribute("width", nodeRadius * 4); + rect.setAttribute("height", nodeRadius * 3); + rect.setAttribute("fill", "red"); + group.appendChild(rect); + + const textElement = document.createElementNS(svgNS, "text"); + textElement.setAttribute("x", nodeRadius * 2); + textElement.setAttribute("y", nodeRadius * 1.5); + textElement.setAttribute("text-anchor", "middle"); + textElement.setAttribute("dominant-baseline", "central"); + textElement.setAttribute("pointer-events", "none"); + textElement.setAttribute("style", "font-size: 20px; fill: white;"); + const textNode = document.createTextNode("+"); + textElement.appendChild(textNode); + group.appendChild(textElement); + group.classList.add("clickable"); + + element.appendChild(group); + let pathCloned = [...path]; + element.addEventListener('click', (event) => { + event.stopPropagation(); + let inserted = false; + for (let current_expaned of expanded) { + if (pathCloned !== undefined && startsWith(pathCloned, current_expaned)) { + current_expaned.splice(0, current_expaned.length, ...pathCloned); + inserted = true; + break; + } + } + if (!inserted) { + expanded.push(path); + } + render_structural_analysis(); + }); + } else if (node_subgraph !== null) { + const subElement = document.createElementNS(svgNS, "g"); + subElement.setAttribute("transform", `translate(${node_layout.x - node_subgraph.layouted.graph().width / 2}, ${node_layout.y - node_subgraph.layouted.graph().height / 2})`); + + const rect = document.createElementNS(svgNS, "rect"); + rect.setAttribute("x", 0); + rect.setAttribute("y", 0); + rect.setAttribute("width", node_subgraph.layouted.graph().width); + rect.setAttribute("height", node_subgraph.layouted.graph().height); + rect.setAttribute("fill", "transparent"); + rect.setAttribute("stroke", "black"); + rect.setAttribute("stroke-width", "1"); + subElement.appendChild(rect); + + const closeG = document.createElementNS(svgNS, "g"); + const closeRect = document.createElementNS(svgNS, "rect"); + closeRect.setAttribute("x", 0); + closeRect.setAttribute("y", 0); + closeRect.setAttribute("width", 32); + closeRect.setAttribute("height", 24); + closeRect.setAttribute("fill", "black"); + + const textElement = document.createElementNS(svgNS, "text"); + textElement.setAttribute("x", 16); + textElement.setAttribute("y", 10); + textElement.setAttribute("text-anchor", "middle"); + textElement.setAttribute("dominant-baseline", "central"); + textElement.setAttribute("pointer-events", "none"); + textElement.setAttribute("style", "font-size: 20px; fill: white;"); + const textNode = document.createTextNode("-"); + textElement.appendChild(textNode); + + closeG.appendChild(closeRect); + closeG.appendChild(textElement); + closeG.classList.add("clickable"); + + let pathCloned = [...path]; + closeG.addEventListener('click', (event) => { + event.stopPropagation(); + for (let current_expaned of expanded) { + if (startsWith(current_expaned, pathCloned)) { + while (current_expaned.length !== pathCloned.length) { + current_expaned.pop(); + } + current_expaned.pop(); + break; + } + } + render_structural_analysis(); + }); + + subElement.appendChild(closeG); + + renderExpandableGraph(subElement, node_subgraph, path); + element.appendChild(subElement); + } else { + console.error("unreachable"); + } + path.pop(); + }); +} + +function render_structural_analysis() { + const code = document.getElementById('code-input').value; + let analysis_result = structural_analysis(code); + fill_holes(analysis_result.graph); + layoutAll(analysis_result.graph, [0]); + const svg = document.getElementById("svg-graph"); + svg.setAttribute("width", analysis_result.graph.layouted.graph().width); + svg.setAttribute("height", analysis_result.graph.layouted.graph().height); + + svg.innerHTML = `<marker id="arrow" markerWidth="5" markerHeight="3.5" refX="4" refY="1.75" orient="auto"> + <polygon points="0 0, 5 1.75, 0 3.5" /> + </marker>`; + const group = document.createElementNS(svgNS, "g"); + group.setAttribute("transform", `translate(0, 0)`); + renderExpandableGraph(group, analysis_result.graph, [0]); + svg.appendChild(group); + + svg.classList.remove('hidden'); + const textOutput = document.getElementById("output"); + textOutput.classList.add('hidden'); +} + +init().then(() => { + document.getElementById('dump-cfg').addEventListener('click', (event) => { + event.stopPropagation(); + const code = document.getElementById('code-input').value; + let cfg = dump_control_flow_graph(code); + let g = new dagre.graphlib.Graph(); + g.setGraph({marginx: nodeRadius, marginy: nodeRadius}); + g.setDefaultEdgeLabel(function() { return {}; }); + cfg.nodes.forEach((node, index) => { + g.setNode( + index, + { + label: index, + width: nodeRadius * 2, + height: nodeRadius * 2 + } + ); + }); + for (let edge of cfg.edges) { + g.setEdge(edge.from, edge.to); + } + dagre.layout(g); + const svg = document.getElementById("svg-graph"); + svg.setAttribute("width", g.graph().width); + svg.setAttribute("height", g.graph().height); + svg.innerHTML = `<marker id="arrow" markerWidth="5" markerHeight="3.5" refX="4" refY="1.75" orient="auto"> + <polygon points="0 0, 5 1.75, 0 3.5" /> + </marker>`; + g.edges().forEach(function(e) { + let edge = g.edge(e); + let points = edge.points; + const path = document.createElementNS(svgNS, "path"); + path.setAttribute("d", `M ${points[0].x} ${points[0].y} Q${points[1].x} ${points[1].y} ${points[2].x} ${points[2].y}`); + path.setAttribute("stroke", "black"); + path.setAttribute("fill", "transparent"); + path.setAttribute("style", "stroke:#000; marker-end: url(#arrow);"); + path.setAttribute("stroke-width", "1.5"); + let edge_info = cfg.edges.find(it => it.from === parseInt(e.v) && it.to === parseInt(e.w)); + if (edge_info.back) { + path.setAttribute('stroke-dasharray', '1.5 1.5'); + } + svg.appendChild(path); + }); + g.nodes().forEach(function(n) { + const group = document.createElementNS(svgNS, "g"); + group.setAttribute("transform", `translate(${g.node(n).x}, ${g.node(n).y})`); + const circle = document.createElementNS(svgNS, "circle"); + circle.setAttribute("r", nodeRadius); + circle.setAttribute("fill", "red"); + group.appendChild(circle); + + const textElement = document.createElementNS(svgNS, "text"); + textElement.setAttribute("text-anchor", "middle"); + textElement.setAttribute("dominant-baseline", "central"); + textElement.setAttribute("fill", "white"); + const textNode = document.createTextNode(cfg.nodes[parseInt(n)]); + textElement.appendChild(textNode); + textElement.setAttribute("pointer-events", "none"); + group.appendChild(textElement); + + svg.appendChild(group); + }); + svg.classList.remove('hidden'); + const textOutput = document.getElementById("output"); + textOutput.classList.add('hidden'); + }); + document.getElementById('optimize').addEventListener('change', () => { + const pass = document.getElementById('optimize').value; + const code = document.getElementById('code-input').value; + const optimized = optimize(code, pass); + const svg = document.getElementById("svg-graph"); + const textOutput = document.getElementById("output"); + textOutput.innerText = optimized; + svg.classList.add('hidden'); + textOutput.classList.remove('hidden'); + }); + document.getElementById('structural-analysis').addEventListener('click', (event) => { + event.stopPropagation(); + render_structural_analysis(); + }); +}); +</script> 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<RegionNode, Vec<(usize, usize)>, usize>, entry: NodeIndex<usize>, } @@ -45,14 +46,14 @@ fn back_edges(c: &FoldedCFG) -> Vec<(NodeIndex<usize>, NodeIndex<usize>)> { result } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] struct If { content: FoldedCFG, on_then: NodeIndex<usize>, on_else: Option<NodeIndex<usize>>, } -#[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::<Vec<_>, _>(|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::<Vec<_>, _>(|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<String>, 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<RegisterName>, 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<Quantity>, } 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<i64> 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<IR> { .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<Type>, 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),