From 1f254f66b0c01e500adbee9fc6624f109d561527 Mon Sep 17 00:00:00 2001 From: Aki Date: Tue, 8 Aug 2023 10:49:21 +0200 Subject: [PATCH 01/11] Implement graphviz --- crates/codegen/src/critical_edge.rs | 2 +- crates/codegen/src/domtree.rs | 4 +- crates/codegen/src/lib.rs | 1 - crates/codegen/src/loop_analysis.rs | 4 +- crates/codegen/src/optim/gvn.rs | 7 +- crates/codegen/src/optim/licm.rs | 7 +- crates/codegen/src/optim/sccp.rs | 4 +- crates/codegen/src/post_domtree.rs | 7 +- crates/filecheck/src/gvn.rs | 4 +- crates/filecheck/src/licm.rs | 6 +- crates/filecheck/src/sccp.rs | 4 +- crates/ir/Cargo.toml | 2 + crates/{codegen => ir}/src/cfg.rs | 6 +- crates/ir/src/dfg.rs | 8 +- crates/ir/src/function.rs | 38 ++++++- crates/ir/src/graphviz/block.rs | 67 ++++++++++++ crates/ir/src/graphviz/function.rs | 136 ++++++++++++++++++++++++ crates/ir/src/graphviz/mod.rs | 74 +++++++++++++ crates/ir/src/insn.rs | 154 +++++++++++++++++++++++++++- crates/ir/src/lib.rs | 4 + crates/ir/src/module.rs | 11 +- crates/ir/src/types.rs | 71 ++++++++++++- crates/ir/src/value.rs | 57 +++++++++- 23 files changed, 634 insertions(+), 44 deletions(-) rename crates/{codegen => ir}/src/cfg.rs (96%) create mode 100644 crates/ir/src/graphviz/block.rs create mode 100644 crates/ir/src/graphviz/function.rs create mode 100644 crates/ir/src/graphviz/mod.rs diff --git a/crates/codegen/src/critical_edge.rs b/crates/codegen/src/critical_edge.rs index 2d46e3da..484813e0 100644 --- a/crates/codegen/src/critical_edge.rs +++ b/crates/codegen/src/critical_edge.rs @@ -1,4 +1,4 @@ -use super::cfg::ControlFlowGraph; +use sonatina_ir::ControlFlowGraph; use sonatina_ir::{ func_cursor::{CursorLocation, FuncCursor, InsnInserter}, diff --git a/crates/codegen/src/domtree.rs b/crates/codegen/src/domtree.rs index b08e458b..9b23c32e 100644 --- a/crates/codegen/src/domtree.rs +++ b/crates/codegen/src/domtree.rs @@ -7,9 +7,7 @@ use std::collections::BTreeSet; use cranelift_entity::{packed_option::PackedOption, SecondaryMap}; -use sonatina_ir::Block; - -use super::cfg::ControlFlowGraph; +use sonatina_ir::{Block, ControlFlowGraph}; #[derive(Default, Debug)] pub struct DomTree { diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index 2bdd2419..eaaa2a70 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -2,7 +2,6 @@ // See and #![allow(clippy::needless_collect)] -pub mod cfg; pub mod critical_edge; pub mod domtree; pub mod loop_analysis; diff --git a/crates/codegen/src/loop_analysis.rs b/crates/codegen/src/loop_analysis.rs index 99dfde9e..0adbbc0b 100644 --- a/crates/codegen/src/loop_analysis.rs +++ b/crates/codegen/src/loop_analysis.rs @@ -2,9 +2,9 @@ use cranelift_entity::{entity_impl, packed_option::PackedOption, PrimaryMap, Sec use fxhash::FxHashMap; use smallvec::SmallVec; -use crate::{cfg::ControlFlowGraph, domtree::DomTree}; +use crate::domtree::DomTree; -use sonatina_ir::Block; +use sonatina_ir::{Block, ControlFlowGraph}; #[derive(Debug, Default)] pub struct LoopTree { diff --git a/crates/codegen/src/optim/gvn.rs b/crates/codegen/src/optim/gvn.rs index d91e449e..7b128a27 100644 --- a/crates/codegen/src/optim/gvn.rs +++ b/crates/codegen/src/optim/gvn.rs @@ -12,15 +12,12 @@ use std::collections::BTreeSet; use cranelift_entity::{entity_impl, packed_option::PackedOption, PrimaryMap, SecondaryMap}; use fxhash::{FxHashMap, FxHashSet}; -use crate::{ - cfg::ControlFlowGraph, - domtree::{DomTree, DominatorTreeTraversable}, -}; +use crate::domtree::{DomTree, DominatorTreeTraversable}; use sonatina_ir::{ func_cursor::{CursorLocation, FuncCursor, InsnInserter}, insn::{BinaryOp, CastOp, InsnData, UnaryOp}, - Block, DataFlowGraph, Function, Immediate, Insn, Type, Value, + Block, ControlFlowGraph, DataFlowGraph, Function, Immediate, Insn, Type, Value, }; use super::{constant_folding, simplify_impl}; diff --git a/crates/codegen/src/optim/licm.rs b/crates/codegen/src/optim/licm.rs index a8892eed..c21ee067 100644 --- a/crates/codegen/src/optim/licm.rs +++ b/crates/codegen/src/optim/licm.rs @@ -1,14 +1,11 @@ // TODO: Add control flow hoisting. use fxhash::{FxHashMap, FxHashSet}; -use crate::{ - cfg::ControlFlowGraph, - loop_analysis::{Loop, LoopTree}, -}; +use crate::loop_analysis::{Loop, LoopTree}; use sonatina_ir::{ func_cursor::{CursorLocation, FuncCursor, InsnInserter}, - Block, Function, Insn, InsnData, Value, + Block, ControlFlowGraph, Function, Insn, InsnData, Value, }; #[derive(Debug)] diff --git a/crates/codegen/src/optim/sccp.rs b/crates/codegen/src/optim/sccp.rs index 157fee64..75c72f55 100644 --- a/crates/codegen/src/optim/sccp.rs +++ b/crates/codegen/src/optim/sccp.rs @@ -8,12 +8,10 @@ use std::{collections::BTreeSet, ops}; use cranelift_entity::SecondaryMap; -use crate::cfg::ControlFlowGraph; - use sonatina_ir::{ func_cursor::{CursorLocation, FuncCursor, InsnInserter}, insn::{BinaryOp, CastOp, InsnData, UnaryOp}, - Block, Function, Immediate, Insn, Type, Value, + Block, ControlFlowGraph, Function, Immediate, Insn, Type, Value, }; #[derive(Debug)] diff --git a/crates/codegen/src/post_domtree.rs b/crates/codegen/src/post_domtree.rs index a2cf0690..9ca59322 100644 --- a/crates/codegen/src/post_domtree.rs +++ b/crates/codegen/src/post_domtree.rs @@ -1,11 +1,8 @@ //! This module contains implementation of `Post Dominator Tree`. -use super::{ - cfg::ControlFlowGraph, - domtree::{DFSet, DomTree}, -}; +use super::domtree::{DFSet, DomTree}; -use sonatina_ir::{Block, Function}; +use sonatina_ir::{Block, ControlFlowGraph, Function}; #[derive(Debug)] pub struct PostDomTree { diff --git a/crates/filecheck/src/gvn.rs b/crates/filecheck/src/gvn.rs index 256de160..a798a875 100644 --- a/crates/filecheck/src/gvn.rs +++ b/crates/filecheck/src/gvn.rs @@ -1,8 +1,8 @@ use std::path::{Path, PathBuf}; -use sonatina_codegen::{cfg::ControlFlowGraph, domtree::DomTree, optim::gvn::GvnSolver}; +use sonatina_codegen::{domtree::DomTree, optim::gvn::GvnSolver}; -use sonatina_ir::Function; +use sonatina_ir::{ControlFlowGraph, Function}; use super::{FuncTransform, FIXTURE_ROOT}; diff --git a/crates/filecheck/src/licm.rs b/crates/filecheck/src/licm.rs index f7e32266..a48fb08c 100644 --- a/crates/filecheck/src/licm.rs +++ b/crates/filecheck/src/licm.rs @@ -1,10 +1,8 @@ use std::path::{Path, PathBuf}; -use sonatina_codegen::{ - cfg::ControlFlowGraph, domtree::DomTree, loop_analysis::LoopTree, optim::licm::LicmSolver, -}; +use sonatina_codegen::{domtree::DomTree, loop_analysis::LoopTree, optim::licm::LicmSolver}; -use sonatina_ir::Function; +use sonatina_ir::{ControlFlowGraph, Function}; use super::{FuncTransform, FIXTURE_ROOT}; diff --git a/crates/filecheck/src/sccp.rs b/crates/filecheck/src/sccp.rs index ad31636c..dc9c9ff4 100644 --- a/crates/filecheck/src/sccp.rs +++ b/crates/filecheck/src/sccp.rs @@ -1,8 +1,8 @@ use std::path::{Path, PathBuf}; -use sonatina_codegen::{cfg::ControlFlowGraph, optim::sccp::SccpSolver}; +use sonatina_codegen::optim::sccp::SccpSolver; -use sonatina_ir::Function; +use sonatina_ir::{ControlFlowGraph, Function}; use super::{FuncTransform, FIXTURE_ROOT}; diff --git a/crates/ir/Cargo.toml b/crates/ir/Cargo.toml index d4279f27..47bccc0b 100644 --- a/crates/ir/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -21,3 +21,5 @@ fxhash = "0.2.1" dyn-clone = "1.0.4" sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } indexmap = "2.0.0" +dot2 = { git = "https://github.com/sanpii/dot2.rs.git" } +id-arena = "2.2.1" diff --git a/crates/codegen/src/cfg.rs b/crates/ir/src/cfg.rs similarity index 96% rename from crates/codegen/src/cfg.rs rename to crates/ir/src/cfg.rs index d4e5687d..cd9c5fea 100644 --- a/crates/codegen/src/cfg.rs +++ b/crates/ir/src/cfg.rs @@ -2,13 +2,13 @@ use std::collections::BTreeSet; use cranelift_entity::{packed_option::PackedOption, SecondaryMap}; -use sonatina_ir::{Block, Function, Insn}; +use crate::{Block, Function, Insn}; #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct ControlFlowGraph { entry: PackedOption, blocks: SecondaryMap, - pub(super) exits: smallvec::SmallVec<[Block; 8]>, + pub exits: smallvec::SmallVec<[Block; 8]>, } impl ControlFlowGraph { @@ -62,7 +62,7 @@ impl ControlFlowGraph { self.blocks[from].remove_succ(to); } - pub(super) fn reverse_edges(&mut self, new_entry: Block, new_exits: &[Block]) { + pub fn reverse_edges(&mut self, new_entry: Block, new_exits: &[Block]) { for node in self.blocks.values_mut() { node.reverse_edge(); } diff --git a/crates/ir/src/dfg.rs b/crates/ir/src/dfg.rs index 0a9b0218..6da96eea 100644 --- a/crates/ir/src/dfg.rs +++ b/crates/ir/src/dfg.rs @@ -1,8 +1,8 @@ //! This module contains Sonatine IR data flow graph. +use std::{collections::BTreeSet, fmt}; use cranelift_entity::{packed_option::PackedOption, PrimaryMap, SecondaryMap}; use fxhash::FxHashMap; -use std::collections::BTreeSet; use crate::{global_variable::ConstantValue, module::ModuleCtx, GlobalVariable}; @@ -308,6 +308,12 @@ pub enum ValueDef { pub struct Block(pub u32); cranelift_entity::entity_impl!(Block); +impl fmt::Display for Block { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "block{}", self.0) + } +} + /// A block data definition. /// A Block data doesn't hold any information for layout of a program. It is managed by /// [`super::layout::Layout`]. diff --git a/crates/ir/src/function.rs b/crates/ir/src/function.rs index 9e269df3..ca8d5ac4 100644 --- a/crates/ir/src/function.rs +++ b/crates/ir/src/function.rs @@ -1,7 +1,9 @@ +use std::fmt::{self, Write}; + use fxhash::FxHashMap; use smallvec::SmallVec; -use crate::{module::ModuleCtx, Linkage}; +use crate::{module::ModuleCtx, types::DisplayType, Linkage}; use super::{module::FuncRef, DataFlowGraph, Layout, Type, Value}; @@ -87,3 +89,37 @@ impl Signature { self.ret_ty = ty; } } + +pub struct DisplaySignature<'a> { + sig: &'a Signature, + dfg: &'a DataFlowGraph, +} + +impl<'a> DisplaySignature<'a> { + pub fn new(sig: &'a Signature, dfg: &'a DataFlowGraph) -> Self { + Self { sig, dfg } + } +} + +impl<'a> fmt::Display for DisplaySignature<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Self { sig, dfg } = *self; + let Signature { + name, + linkage, + args, + ret_ty, + } = sig; + + let mut args_ty = String::new(); + for arg_ty in args { + let ty = DisplayType::new(*arg_ty, dfg); + write!(&mut args_ty, "{ty} ")?; + } + let args_ty = args_ty.trim(); + + let ret_ty = DisplayType::new(*ret_ty, dfg); + + write!(f, "func {linkage} %{name}({args_ty}) -> {ret_ty}") + } +} diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs new file mode 100644 index 00000000..e1a678b2 --- /dev/null +++ b/crates/ir/src/graphviz/block.rs @@ -0,0 +1,67 @@ +use std::fmt::Write; + +use dot2::label; + +use crate::{function::DisplaySignature, insn::DisplayInsn, Block, ControlFlowGraph, Function}; + +#[derive(Debug, Clone, Copy)] +pub(super) struct BlockNode<'a> +where + Self: 'a, +{ + pub func: &'a Function, + pub block: Block, +} + +impl<'a> BlockNode<'a> { + pub(super) fn new(func: &'a Function, block: Block) -> Self { + Self { func, block } + } + + pub(super) fn succs(self) -> Vec { + let mut cfg = ControlFlowGraph::new(); + cfg.compute(self.func); + cfg.succs_of(self.block) + .map(|block| BlockNode::new(self.func, *block)) + .collect() + } +} + +impl<'a> BlockNode<'a> { + pub(super) fn label(self) -> label::Text<'static> { + let Self { block, func } = self; + let Function { + sig, dfg, layout, .. + } = func; + if block.0 == u32::MAX { + let sig = DisplaySignature::new(sig, dfg); + return label::Text::LabelStr(format!("{sig}").into()); + } + + let mut label = r#""#.to_string(); + + // Write block header. + write!( + &mut label, + r#""#, + block + ) + .unwrap(); + + // Write block body. + write!(label, r#""#).unwrap(); + + write!(label, "
{}
"#).unwrap(); + for insn in layout.iter_insn(self.block) { + let display_insn = DisplayInsn::new(insn, dfg); + let mut insn_string = String::new(); + write!(&mut insn_string, "{}", display_insn).unwrap(); + + write!(label, "{}", dot2::escape_html(&insn_string)).unwrap(); + write!(label, "
").unwrap(); + } + write!(label, r#"
").unwrap(); + + label::Text::HtmlStr(label.into()) + } +} diff --git a/crates/ir/src/graphviz/function.rs b/crates/ir/src/graphviz/function.rs new file mode 100644 index 00000000..d7ad424e --- /dev/null +++ b/crates/ir/src/graphviz/function.rs @@ -0,0 +1,136 @@ +use std::iter; + +use dot2::{label::Text, GraphWalk, Id, Labeller, Style}; + +use crate::{value::DisplayArgValues, Block, ControlFlowGraph, Function, InsnData}; + +use super::block::BlockNode; + +pub(super) struct FunctionGraph<'a>(pub &'a Function) +where + Self: 'a; + +impl<'a> FunctionGraph<'a> { + pub(super) fn blocks(&self) -> Vec> { + let Self(func) = *self; + let dummy_block = Block(u32::MAX); + let mut cfg = ControlFlowGraph::new(); + cfg.compute(func); + cfg.post_order() + .map(|block| BlockNode::new(func, block)) + .chain(iter::once(BlockNode::new(func, dummy_block))) + .collect() + } +} + +impl<'a> Labeller<'a> for FunctionGraph<'a> { + type Node = BlockNode<'a>; + type Edge = BlockEdge<'a>; + type Subgraph = (); + + fn graph_id(&self) -> dot2::Result> { + let Self(func) = *self; + let sig_name = func.sig.name().to_string(); + Id::new(sig_name) + } + + fn node_id(&self, n: &Self::Node) -> dot2::Result> { + let block = n.block; + if block.0 == u32::MAX { + return dot2::Id::new("dummy_block"); + } + dot2::Id::new(format!("{block}")) + } + + fn node_shape(&self, _n: &Self::Node) -> Option> { + Some(Text::LabelStr("none".into())) + } + + fn edge_style(&'a self, e: &Self::Edge) -> Style { + if e.from.block.0 == u32::MAX { + Style::Invisible + } else { + Style::None + } + } + + fn node_label(&'a self, n: &Self::Node) -> dot2::Result> { + Ok(n.label()) + } + + fn edge_label(&self, e: &Self::Edge) -> Text<'a> { + e.label() + } +} + +impl<'a> GraphWalk<'a> for FunctionGraph<'a> { + type Node = BlockNode<'a>; + type Edge = BlockEdge<'a>; + type Subgraph = (); + + fn nodes(&self) -> dot2::Nodes<'a, Self::Node> { + self.blocks().into() + } + + fn edges(&'a self) -> dot2::Edges<'a, Self::Edge> { + let Self(func) = *self; + let mut blocks = self.blocks(); + + let dummy_block = blocks.pop().unwrap(); + let mut edges = vec![BlockEdge { + from: dummy_block, + to: BlockNode::new(func, Block(0u32)), + func, + }]; + for block in blocks { + for succ in block.succs() { + let edge = BlockEdge { + from: block, + to: succ, + func, + }; + edges.push(edge); + } + } + + edges.into() + } + + fn source(&self, edge: &Self::Edge) -> Self::Node { + edge.from + } + + fn target(&self, edge: &Self::Edge) -> Self::Node { + edge.to + } +} + +#[derive(Debug, Clone, Copy)] +pub(super) struct BlockEdge<'a> +where + Self: 'a, +{ + from: BlockNode<'a>, + to: BlockNode<'a>, + func: &'a Function, +} + +impl<'a> BlockEdge<'a> { + fn label(self) -> Text<'static> { + let Self { from, to, func } = self; + let to = to.block; + let from = from.block; + for insn in func.layout.iter_insn(to) { + if let InsnData::Phi { values, blocks, .. } = func.dfg.insn_data(insn) { + for (i, block) in blocks.into_iter().enumerate() { + if *block == from { + let flow_arg = [values[i]]; + let v = DisplayArgValues::new(&flow_arg, &func.dfg); + return Text::LabelStr(format!("{v}").into()); + } + } + } + } + Text::LabelStr("".into()) + } +} diff --git a/crates/ir/src/graphviz/mod.rs b/crates/ir/src/graphviz/mod.rs new file mode 100644 index 00000000..84abb4c7 --- /dev/null +++ b/crates/ir/src/graphviz/mod.rs @@ -0,0 +1,74 @@ +use std::io; + +use crate::Function; + +mod block; +mod function; + +pub fn render_to(func: &Function, output: &mut W) -> io::Result<()> { + let func_graph = function::FunctionGraph(func); + dot2::render(&func_graph, output).map_err(|err| match err { + dot2::Error::Io(err) => err, + _ => panic!("invalid graphviz id"), + }) +} + +#[cfg(test)] +mod test { + use crate::{builder, Type}; + + use super::*; + + #[test] + fn test_dump_ir() { + let mut test_module_builder = builder::test_util::TestModuleBuilder::new(); + let mut builder = test_module_builder.func_builder(&[Type::I64], Type::Void); + + let entry_block = builder.append_block(); + let then_block = builder.append_block(); + let else_block = builder.append_block(); + let merge_block = builder.append_block(); + + let arg0 = builder.args()[0]; + + builder.switch_to_block(entry_block); + builder.br(arg0, then_block, else_block); + + builder.switch_to_block(then_block); + let v1 = builder.make_imm_value(1i64); + builder.jump(merge_block); + + builder.switch_to_block(else_block); + let v2 = builder.make_imm_value(2i64); + builder.jump(merge_block); + + builder.switch_to_block(merge_block); + let v3 = builder.phi(&[(v1, then_block), (v2, else_block)]); + builder.add(v3, arg0); + builder.ret(None); + + builder.seal_all(); + let func_ref = builder.finish(); + let module = test_module_builder.build(); + + let mut text = vec![]; + render_to(&module.funcs[func_ref], &mut text).unwrap(); + + assert_eq!( + text, + b"digraph test_func { + block3[label=<
block3
v3.i64 = phi (1.i64 block1) (2.i64 block2);
v4.i64 = add v3 v0;
ret ;
>][shape=\"none\"]; + block2[label=<
block2
jump block3;
>][shape=\"none\"]; + block1[label=<
block1
jump block3;
>][shape=\"none\"]; + block0[label=<
block0
branch v0 block1 block2;
>][shape=\"none\"]; + dummy_block[label=\"func public %test_func(i64) -> ()\"][shape=\"none\"]; + dummy_block -> block0[label=\"\"][style=\"invis\"]; + block2 -> block3[label=\"2.i64\"]; + block1 -> block3[label=\"1.i64\"]; + block0 -> block1[label=\"\"]; + block0 -> block2[label=\"\"]; +} +" + ); + } +} diff --git a/crates/ir/src/insn.rs b/crates/ir/src/insn.rs index 7596971e..32f031e9 100644 --- a/crates/ir/src/insn.rs +++ b/crates/ir/src/insn.rs @@ -1,11 +1,14 @@ //! This module contains Sonatine IR instructions definitions. // TODO: Add type checker for instruction arguments. -use std::fmt; +use std::fmt::{self, Write}; use smallvec::SmallVec; -use crate::types::CompoundTypeData; +use crate::{ + types::{CompoundTypeData, DisplayType}, + value::{DisplayArgValues, DisplayResultValue}, +}; use super::{module::FuncRef, Block, DataFlowGraph, Type, Value, ValueData}; @@ -14,6 +17,31 @@ use super::{module::FuncRef, Block, DataFlowGraph, Type, Value, ValueData}; pub struct Insn(pub u32); cranelift_entity::entity_impl!(Insn); +pub struct DisplayInsn<'a> { + insn: Insn, + dfg: &'a DataFlowGraph, +} + +impl<'a> DisplayInsn<'a> { + pub fn new(insn: Insn, dfg: &'a DataFlowGraph) -> Self { + Self { insn, dfg } + } +} + +impl<'a> fmt::Display for DisplayInsn<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Self { insn, dfg } = *self; + + let mut result_string = String::new(); + let display_result = DisplayResultValue::new(insn, dfg); + write!(&mut result_string, "{display_result}")?; + + let display_insn = DisplayInsnData::new(insn, dfg); + + write!(f, "{result_string}{display_insn}") + } +} + /// An instruction data definition. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum InsnData { @@ -105,6 +133,20 @@ pub enum DataLocationKind { Storage, } +impl fmt::Display for DataLocationKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use DataLocationKind::*; + write!( + f, + "{}", + match self { + Memory => "mem", + Storage => "store", + } + ) + } +} + impl InsnData { pub fn unary(code: UnaryOp, lhs: Value) -> Self { Self::Unary { code, args: [lhs] } @@ -376,6 +418,114 @@ impl InsnData { } } +pub struct DisplayInsnData<'a> { + insn: Insn, + dfg: &'a DataFlowGraph, +} + +impl<'a> DisplayInsnData<'a> { + pub fn new(insn: Insn, dfg: &'a DataFlowGraph) -> Self { + Self { insn, dfg } + } +} + +impl<'a> fmt::Display for DisplayInsnData<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use InsnData::*; + let Self { insn, dfg } = *self; + let insn_data = dfg.insn_data(insn); + match insn_data { + Unary { code, args } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "{} v{v}.;", code.as_str()) + } + Binary { code, args } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "{} {v};", code.as_str(),) + } + Cast { code, args, ty } => { + let v = DisplayArgValues::new(args, dfg); + let display_ty = DisplayType::new(*ty, dfg); + write!(f, "{} {v} {display_ty};", code.as_str()) + } + Load { args, loc } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "load {v} {loc};") + } + Store { args, loc } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "store {v} {loc};") + } + Call { + args, + func, + ret_ty: ty, + } => { + let v = DisplayArgValues::new(args, dfg); + let display_ty = DisplayType::new(*ty, dfg); + write!(f, "call %{func} {v} {display_ty};") + } + Jump { code, dests } => { + let block = dests[0]; + write!(f, "{code} {block};") + } + Branch { args, dests } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "branch {v} {} {};", dests[0], dests[1]) + } + BrTable { + args, + default, + table, + } => { + let v = DisplayArgValues::new(args, dfg); + + let mut default_string = String::new(); + if let Some(block) = default { + write!(&mut default_string, "{block} ")?; + } + + let mut blocks_string = String::new(); + write!(&mut blocks_string, "{}", table[0])?; + for block in &table[1..] { + write!(&mut blocks_string, ",{block}")?; + } + + write!(f, "br_table {v} {default_string} {blocks_string};") + } + Alloca { ty } => { + let display_ty = DisplayType::new(*ty, dfg); + write!(f, "alloca {display_ty};") + } + Return { args } => { + let mut args_string = String::new(); + if let Some(arg) = *args { + let arg = [arg]; + let v = DisplayArgValues::new(&arg, dfg); + write!(&mut args_string, "{v}")?; + } + + write!(f, "ret {args_string};") + } + Gep { args } => { + let v = DisplayArgValues::new(args, dfg); + write!(f, "gep {v};") + } + Phi { values, blocks, .. } => { + let mut values_string = String::new(); + let v = DisplayArgValues::new(values, dfg); + write!(&mut values_string, "{}", v)?; + + let mut args_string = String::new(); + for (value, block) in values_string.split_whitespace().zip(blocks.into_iter()) { + write!(&mut args_string, " ({value} {block})")?; + } + + write!(f, "phi{args_string};",) + } + } + } +} /// Unary operations. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum UnaryOp { diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index ae03029a..d36813f2 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -1,8 +1,10 @@ pub mod builder; +pub mod cfg; pub mod dfg; pub mod func_cursor; pub mod function; pub mod global_variable; +pub mod graphviz; pub mod insn; pub mod ir_writer; pub mod isa; @@ -16,9 +18,11 @@ mod bigint; pub use bigint::{I256, U256}; pub use builder::Variable; +pub use cfg::ControlFlowGraph; pub use dfg::{Block, BlockData, DataFlowGraph}; pub use function::{Function, Signature}; pub use global_variable::{GlobalVariable, GlobalVariableData}; +pub use graphviz::render_to; pub use insn::{BranchInfo, DataLocationKind, Insn, InsnData}; pub use layout::Layout; pub use linkage::Linkage; diff --git a/crates/ir/src/module.rs b/crates/ir/src/module.rs index 24f0c98c..2e82c99b 100644 --- a/crates/ir/src/module.rs +++ b/crates/ir/src/module.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::{ + fmt, + sync::{Arc, Mutex}, +}; use cranelift_entity::{entity_impl, PrimaryMap}; @@ -86,3 +89,9 @@ impl ModuleCtx { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FuncRef(u32); entity_impl!(FuncRef); + +impl fmt::Display for FuncRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/crates/ir/src/types.rs b/crates/ir/src/types.rs index f83d2c9e..eeff9c60 100644 --- a/crates/ir/src/types.rs +++ b/crates/ir/src/types.rs @@ -1,11 +1,15 @@ //! This module contains Sonatina IR types definitions. - -use std::cmp; +use std::{ + cmp, + fmt::{self, Write}, +}; use cranelift_entity::PrimaryMap; use fxhash::FxHashMap; use indexmap::IndexMap; +use crate::DataFlowGraph; + #[derive(Debug, Default)] pub struct TypeStore { compounds: PrimaryMap, @@ -138,6 +142,69 @@ pub enum Type { pub struct CompoundType(u32); cranelift_entity::entity_impl!(CompoundType); +struct DisplayCompoundType<'a> { + cmpd_ty: CompoundType, + dfg: &'a DataFlowGraph, +} + +impl<'a> fmt::Display for DisplayCompoundType<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use CompoundTypeData::*; + let dfg = self.dfg; + dfg.ctx + .with_ty_store(|s| match s.resolve_compound(self.cmpd_ty) { + Array { elem: ty, len } => { + let display_ty = DisplayType::new(*ty, dfg); + write!(f, "[{display_ty};{len}]") + } + Ptr(ty) => { + let display_ty = DisplayType::new(*ty, dfg); + write!(f, "*{display_ty}") + } + Struct(StructData { name, packed, .. }) => { + let mut struct_string = String::new(); + write!(&mut struct_string, "{{{name}}}")?; + if *packed { + write!(f, "<{struct_string}>") + } else { + write!(f, "{struct_string}") + } + } + }) + } +} + +pub struct DisplayType<'a> { + ty: Type, + dfg: &'a DataFlowGraph, +} + +impl<'a> DisplayType<'a> { + pub fn new(ty: Type, dfg: &'a DataFlowGraph) -> Self { + Self { ty, dfg } + } +} + +impl<'a> fmt::Display for DisplayType<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Type::*; + match self.ty { + I1 => write!(f, "i1"), + I8 => write!(f, "i8"), + I16 => write!(f, "i16"), + I32 => write!(f, "i32"), + I64 => write!(f, "i64"), + I128 => write!(f, "i128"), + I256 => write!(f, "i256"), + Compound(cmpd_ty) => { + let dfg = self.dfg; + write!(f, "{}", DisplayCompoundType { cmpd_ty, dfg }) + } + Void => write!(f, "()"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CompoundTypeData { Array { elem: Type, len: usize }, diff --git a/crates/ir/src/value.rs b/crates/ir/src/value.rs index 5f106a0e..01ce2703 100644 --- a/crates/ir/src/value.rs +++ b/crates/ir/src/value.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; -use crate::GlobalVariable; +use crate::{types::DisplayType, DataFlowGraph, GlobalVariable}; use super::{Insn, Type, I256, U256}; @@ -11,6 +11,61 @@ use super::{Insn, Type, I256, U256}; pub struct Value(pub u32); cranelift_entity::entity_impl!(Value); +pub struct DisplayResultValue<'a> { + insn: Insn, + dfg: &'a DataFlowGraph, +} + +impl<'a> DisplayResultValue<'a> { + pub fn new(insn: Insn, dfg: &'a DataFlowGraph) -> Self { + Self { insn, dfg } + } +} + +impl<'a> fmt::Display for DisplayResultValue<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let Self { insn, dfg } = *self; + if let Some(value) = dfg.insn_result(insn) { + let ty = dfg.insn_result_ty(insn).unwrap(); + let display_ty = DisplayType::new(ty, dfg); + return write!(f, "v{}.{display_ty} = ", value.0); + } + Ok(()) + } +} + +pub struct DisplayArgValues<'a, 'b> { + args: &'a [Value], + dfg: &'b DataFlowGraph, +} + +impl<'a, 'b> DisplayArgValues<'a, 'b> { + pub fn new(args: &'a [Value], dfg: &'b DataFlowGraph) -> Self { + Self { args, dfg } + } + + fn write_arg(&self, w: &mut W, arg: &Value) -> fmt::Result { + let dfg = self.dfg; + match *dfg.value_data(*arg) { + ValueData::Immediate { imm, ty } => { + let display_ty = DisplayType::new(ty, dfg); + write!(w, "{imm}.{display_ty} ") + } + _ => write!(w, "v{} ", arg.0), + } + } +} + +impl<'a, 'b> fmt::Display for DisplayArgValues<'a, 'b> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut args_string = String::new(); + for arg in self.args { + self.write_arg(&mut args_string, arg)?; + } + write!(f, "{}", args_string.trim()) + } +} + /// An value data definition. #[derive(Debug, Clone)] pub enum ValueData { From ce286dfb25ad6f996002320b19bf8913bb618270 Mon Sep 17 00:00:00 2001 From: Aki <140970520+k0aki@users.noreply.github.com> Date: Mon, 14 Aug 2023 09:07:19 +0200 Subject: [PATCH 02/11] Remove unused dep Co-authored-by: Yoshitomo Nakanishi --- crates/ir/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ir/Cargo.toml b/crates/ir/Cargo.toml index 47bccc0b..92543f43 100644 --- a/crates/ir/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -22,4 +22,3 @@ dyn-clone = "1.0.4" sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } indexmap = "2.0.0" dot2 = { git = "https://github.com/sanpii/dot2.rs.git" } -id-arena = "2.2.1" From 307029bb6e7988df2bd6472988b4d68dcd92593e Mon Sep 17 00:00:00 2001 From: Aki <140970520+k0aki@users.noreply.github.com> Date: Mon, 14 Aug 2023 09:16:17 +0200 Subject: [PATCH 03/11] Remove implied lifetime bound and restrict visibility Co-authored-by: Yoshitomo Nakanishi --- crates/ir/src/graphviz/block.rs | 9 +++------ crates/ir/src/graphviz/function.rs | 9 ++------- crates/ir/src/graphviz/mod.rs | 4 +++- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs index e1a678b2..c8f1d892 100644 --- a/crates/ir/src/graphviz/block.rs +++ b/crates/ir/src/graphviz/block.rs @@ -5,12 +5,9 @@ use dot2::label; use crate::{function::DisplaySignature, insn::DisplayInsn, Block, ControlFlowGraph, Function}; #[derive(Debug, Clone, Copy)] -pub(super) struct BlockNode<'a> -where - Self: 'a, -{ - pub func: &'a Function, - pub block: Block, +pub(super) struct BlockNode<'a> { + pub(super) func: &'a Function, + pub(super) block: Block, } impl<'a> BlockNode<'a> { diff --git a/crates/ir/src/graphviz/function.rs b/crates/ir/src/graphviz/function.rs index d7ad424e..82a0cc4b 100644 --- a/crates/ir/src/graphviz/function.rs +++ b/crates/ir/src/graphviz/function.rs @@ -6,9 +6,7 @@ use crate::{value::DisplayArgValues, Block, ControlFlowGraph, Function, InsnData use super::block::BlockNode; -pub(super) struct FunctionGraph<'a>(pub &'a Function) -where - Self: 'a; +pub(super) struct FunctionGraph<'a>(pub(super) &'a Function); impl<'a> FunctionGraph<'a> { pub(super) fn blocks(&self) -> Vec> { @@ -106,10 +104,7 @@ impl<'a> GraphWalk<'a> for FunctionGraph<'a> { } #[derive(Debug, Clone, Copy)] -pub(super) struct BlockEdge<'a> -where - Self: 'a, -{ +pub(super) struct BlockEdge<'a> { from: BlockNode<'a>, to: BlockNode<'a>, func: &'a Function, diff --git a/crates/ir/src/graphviz/mod.rs b/crates/ir/src/graphviz/mod.rs index 84abb4c7..b132bf51 100644 --- a/crates/ir/src/graphviz/mod.rs +++ b/crates/ir/src/graphviz/mod.rs @@ -5,8 +5,10 @@ use crate::Function; mod block; mod function; +use function::FunctionGraph; + pub fn render_to(func: &Function, output: &mut W) -> io::Result<()> { - let func_graph = function::FunctionGraph(func); + let func_graph = FunctionGraph(func); dot2::render(&func_graph, output).map_err(|err| match err { dot2::Error::Io(err) => err, _ => panic!("invalid graphviz id"), From 6ba9ab6dc930e0fad04ec64307d256ec852dfcbb Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 15:22:36 +0200 Subject: [PATCH 04/11] Avoid heap allocation for string --- crates/ir/src/graphviz/mod.rs | 2 +- crates/ir/src/insn.rs | 49 ++++++++++++++--------------------- crates/ir/src/types.rs | 11 +++----- crates/ir/src/value.rs | 15 ++++++----- 4 files changed, 31 insertions(+), 46 deletions(-) diff --git a/crates/ir/src/graphviz/mod.rs b/crates/ir/src/graphviz/mod.rs index b132bf51..1070d78f 100644 --- a/crates/ir/src/graphviz/mod.rs +++ b/crates/ir/src/graphviz/mod.rs @@ -59,7 +59,7 @@ mod test { assert_eq!( text, b"digraph test_func { - block3[label=<
block3
v3.i64 = phi (1.i64 block1) (2.i64 block2);
v4.i64 = add v3 v0;
ret ;
>][shape=\"none\"]; + block3[label=<
block3
v3.i64 = phi (1.i64 block1) (2.i64 block2);
v4.i64 = add v3 v0;
ret;
>][shape=\"none\"]; block2[label=<
block2
jump block3;
>][shape=\"none\"]; block1[label=<
block1
jump block3;
>][shape=\"none\"]; block0[label=<
block0
branch v0 block1 block2;
>][shape=\"none\"]; diff --git a/crates/ir/src/insn.rs b/crates/ir/src/insn.rs index 32f031e9..84e0d67a 100644 --- a/crates/ir/src/insn.rs +++ b/crates/ir/src/insn.rs @@ -1,7 +1,7 @@ //! This module contains Sonatine IR instructions definitions. // TODO: Add type checker for instruction arguments. -use std::fmt::{self, Write}; +use std::fmt; use smallvec::SmallVec; @@ -32,13 +32,11 @@ impl<'a> fmt::Display for DisplayInsn<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let Self { insn, dfg } = *self; - let mut result_string = String::new(); let display_result = DisplayResultValue::new(insn, dfg); - write!(&mut result_string, "{display_result}")?; + write!(f, "{display_result}")?; let display_insn = DisplayInsnData::new(insn, dfg); - - write!(f, "{result_string}{display_insn}") + write!(f, "{display_insn}") } } @@ -479,49 +477,40 @@ impl<'a> fmt::Display for DisplayInsnData<'a> { table, } => { let v = DisplayArgValues::new(args, dfg); - - let mut default_string = String::new(); + write!(f, "br_table {v}")?; if let Some(block) = default { - write!(&mut default_string, "{block} ")?; + write!(f, " {block}")?; } - - let mut blocks_string = String::new(); - write!(&mut blocks_string, "{}", table[0])?; - for block in &table[1..] { - write!(&mut blocks_string, ",{block}")?; + for block in &table[..table.len() - 2] { + write!(f, " {block}")?; } - - write!(f, "br_table {v} {default_string} {blocks_string};") + write!(f, " {};", table[table.len() - 1]) } Alloca { ty } => { let display_ty = DisplayType::new(*ty, dfg); write!(f, "alloca {display_ty};") } Return { args } => { - let mut args_string = String::new(); - if let Some(arg) = *args { - let arg = [arg]; + write!(f, "ret")?; + if let Some(arg) = args { + let arg = [*arg]; let v = DisplayArgValues::new(&arg, dfg); - write!(&mut args_string, "{v}")?; + write!(f, " {v}")?; } - - write!(f, "ret {args_string};") + write!(f, ";") } Gep { args } => { let v = DisplayArgValues::new(args, dfg); write!(f, "gep {v};") } Phi { values, blocks, .. } => { - let mut values_string = String::new(); - let v = DisplayArgValues::new(values, dfg); - write!(&mut values_string, "{}", v)?; - - let mut args_string = String::new(); - for (value, block) in values_string.split_whitespace().zip(blocks.into_iter()) { - write!(&mut args_string, " ({value} {block})")?; + write!(f, "phi")?; + for (value, block) in values.iter().zip(blocks.iter()) { + let value = [*value]; + let v = DisplayArgValues::new(&value, dfg); + write!(f, " ({v} {block})")?; } - - write!(f, "phi{args_string};",) + write!(f, ";") } } } diff --git a/crates/ir/src/types.rs b/crates/ir/src/types.rs index eeff9c60..0cdff8e4 100644 --- a/crates/ir/src/types.rs +++ b/crates/ir/src/types.rs @@ -1,8 +1,5 @@ //! This module contains Sonatina IR types definitions. -use std::{ - cmp, - fmt::{self, Write}, -}; +use std::{cmp, fmt}; use cranelift_entity::PrimaryMap; use fxhash::FxHashMap; @@ -162,12 +159,10 @@ impl<'a> fmt::Display for DisplayCompoundType<'a> { write!(f, "*{display_ty}") } Struct(StructData { name, packed, .. }) => { - let mut struct_string = String::new(); - write!(&mut struct_string, "{{{name}}}")?; if *packed { - write!(f, "<{struct_string}>") + write!(f, "<{{{name}}}>") } else { - write!(f, "{struct_string}") + write!(f, "{{{name}}}") } } }) diff --git a/crates/ir/src/value.rs b/crates/ir/src/value.rs index 01ce2703..00656600 100644 --- a/crates/ir/src/value.rs +++ b/crates/ir/src/value.rs @@ -44,25 +44,26 @@ impl<'a, 'b> DisplayArgValues<'a, 'b> { Self { args, dfg } } - fn write_arg(&self, w: &mut W, arg: &Value) -> fmt::Result { + pub fn write_arg(&self, w: &mut W, arg: &Value) -> fmt::Result { let dfg = self.dfg; match *dfg.value_data(*arg) { ValueData::Immediate { imm, ty } => { let display_ty = DisplayType::new(ty, dfg); - write!(w, "{imm}.{display_ty} ") + write!(w, "{imm}.{display_ty}") } - _ => write!(w, "v{} ", arg.0), + _ => write!(w, "v{}", arg.0), } } } impl<'a, 'b> fmt::Display for DisplayArgValues<'a, 'b> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut args_string = String::new(); - for arg in self.args { - self.write_arg(&mut args_string, arg)?; + self.write_arg(f, &self.args[0])?; + for arg in &self.args[1..] { + write!(f, " ")?; + self.write_arg(f, arg)?; } - write!(f, "{}", args_string.trim()) + Ok(()) } } From cb5ab25296f69a9350e6bb3a6f912d630d491686 Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 15:35:02 +0200 Subject: [PATCH 05/11] Assign dummy block --- crates/ir/src/graphviz/block.rs | 4 +++- crates/ir/src/graphviz/function.rs | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs index c8f1d892..3be2058d 100644 --- a/crates/ir/src/graphviz/block.rs +++ b/crates/ir/src/graphviz/block.rs @@ -4,6 +4,8 @@ use dot2::label; use crate::{function::DisplaySignature, insn::DisplayInsn, Block, ControlFlowGraph, Function}; +use super::function::DUMMY_BLOCK; + #[derive(Debug, Clone, Copy)] pub(super) struct BlockNode<'a> { pub(super) func: &'a Function, @@ -30,7 +32,7 @@ impl<'a> BlockNode<'a> { let Function { sig, dfg, layout, .. } = func; - if block.0 == u32::MAX { + if block == DUMMY_BLOCK { let sig = DisplaySignature::new(sig, dfg); return label::Text::LabelStr(format!("{sig}").into()); } diff --git a/crates/ir/src/graphviz/function.rs b/crates/ir/src/graphviz/function.rs index 82a0cc4b..c4db3f88 100644 --- a/crates/ir/src/graphviz/function.rs +++ b/crates/ir/src/graphviz/function.rs @@ -6,17 +6,20 @@ use crate::{value::DisplayArgValues, Block, ControlFlowGraph, Function, InsnData use super::block::BlockNode; +pub(super) const DUMMY_BLOCK: Block = Block(u32::MAX); + pub(super) struct FunctionGraph<'a>(pub(super) &'a Function); impl<'a> FunctionGraph<'a> { pub(super) fn blocks(&self) -> Vec> { let Self(func) = *self; - let dummy_block = Block(u32::MAX); let mut cfg = ControlFlowGraph::new(); cfg.compute(func); + // Dummy block is needed to label the graph with the function signature. Returns a vector + // with the dummy block as a last element. cfg.post_order() .map(|block| BlockNode::new(func, block)) - .chain(iter::once(BlockNode::new(func, dummy_block))) + .chain(iter::once(BlockNode::new(func, DUMMY_BLOCK))) .collect() } } @@ -34,7 +37,7 @@ impl<'a> Labeller<'a> for FunctionGraph<'a> { fn node_id(&self, n: &Self::Node) -> dot2::Result> { let block = n.block; - if block.0 == u32::MAX { + if block == DUMMY_BLOCK { return dot2::Id::new("dummy_block"); } dot2::Id::new(format!("{block}")) @@ -45,7 +48,7 @@ impl<'a> Labeller<'a> for FunctionGraph<'a> { } fn edge_style(&'a self, e: &Self::Edge) -> Style { - if e.from.block.0 == u32::MAX { + if e.from.block == DUMMY_BLOCK { Style::Invisible } else { Style::None From 25c1429ca322d53dcf85baac9a15b3a72cefeee9 Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 16:20:30 +0200 Subject: [PATCH 06/11] Display callees --- crates/ir/src/insn.rs | 32 +++++++++++++++++++------------- crates/ir/src/module.rs | 17 +++++++++++++++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/crates/ir/src/insn.rs b/crates/ir/src/insn.rs index 84e0d67a..5b3e8804 100644 --- a/crates/ir/src/insn.rs +++ b/crates/ir/src/insn.rs @@ -6,11 +6,15 @@ use std::fmt; use smallvec::SmallVec; use crate::{ + function::Function, types::{CompoundTypeData, DisplayType}, value::{DisplayArgValues, DisplayResultValue}, }; -use super::{module::FuncRef, Block, DataFlowGraph, Type, Value, ValueData}; +use super::{ + module::{DisplayCalleeFuncRef, FuncRef}, + Block, DataFlowGraph, Type, Value, ValueData, +}; /// An opaque reference to [`InsnData`] #[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)] @@ -19,23 +23,23 @@ cranelift_entity::entity_impl!(Insn); pub struct DisplayInsn<'a> { insn: Insn, - dfg: &'a DataFlowGraph, + func: &'a Function, } impl<'a> DisplayInsn<'a> { - pub fn new(insn: Insn, dfg: &'a DataFlowGraph) -> Self { - Self { insn, dfg } + pub fn new(insn: Insn, func: &'a Function) -> Self { + Self { insn, func } } } impl<'a> fmt::Display for DisplayInsn<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Self { insn, dfg } = *self; + let Self { insn, func } = *self; - let display_result = DisplayResultValue::new(insn, dfg); + let display_result = DisplayResultValue::new(insn, &func.dfg); write!(f, "{display_result}")?; - let display_insn = DisplayInsnData::new(insn, dfg); + let display_insn = DisplayInsnData::new(insn, func); write!(f, "{display_insn}") } } @@ -418,19 +422,20 @@ impl InsnData { pub struct DisplayInsnData<'a> { insn: Insn, - dfg: &'a DataFlowGraph, + func: &'a Function, } impl<'a> DisplayInsnData<'a> { - pub fn new(insn: Insn, dfg: &'a DataFlowGraph) -> Self { - Self { insn, dfg } + pub fn new(insn: Insn, func: &'a Function) -> Self { + Self { insn, func } } } impl<'a> fmt::Display for DisplayInsnData<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use InsnData::*; - let Self { insn, dfg } = *self; + let Self { insn, func } = *self; + let dfg = &func.dfg; let insn_data = dfg.insn_data(insn); match insn_data { Unary { code, args } => { @@ -456,12 +461,13 @@ impl<'a> fmt::Display for DisplayInsnData<'a> { } Call { args, - func, + func: callee, ret_ty: ty, } => { let v = DisplayArgValues::new(args, dfg); + let callee = DisplayCalleeFuncRef::new(callee, func); let display_ty = DisplayType::new(*ty, dfg); - write!(f, "call %{func} {v} {display_ty};") + write!(f, "call %{callee} {v} {display_ty};") } Jump { code, dests } => { let block = dests[0]; diff --git a/crates/ir/src/module.rs b/crates/ir/src/module.rs index 2e82c99b..c9bef630 100644 --- a/crates/ir/src/module.rs +++ b/crates/ir/src/module.rs @@ -90,8 +90,21 @@ impl ModuleCtx { pub struct FuncRef(u32); entity_impl!(FuncRef); -impl fmt::Display for FuncRef { +pub struct DisplayCalleeFuncRef<'a> { + callee: &'a FuncRef, + func: &'a Function, +} + +impl<'a> DisplayCalleeFuncRef<'a> { + pub fn new(callee: &'a FuncRef, func: &'a Function) -> Self { + Self { callee, func } + } +} + +impl<'a> fmt::Display for DisplayCalleeFuncRef<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + let Self { callee, func } = *self; + let sig = func.callees.get(callee).unwrap(); + write!(f, "{}", sig.name()) } } From 9a80406648118dec0394cace3e79f8a5598ae7f3 Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 16:34:21 +0200 Subject: [PATCH 07/11] Compute control flow graph once per function --- crates/ir/src/graphviz/block.rs | 11 ++++++----- crates/ir/src/graphviz/function.rs | 25 ++++++++++++++++--------- crates/ir/src/graphviz/mod.rs | 6 ++++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs index 3be2058d..314760e6 100644 --- a/crates/ir/src/graphviz/block.rs +++ b/crates/ir/src/graphviz/block.rs @@ -9,26 +9,27 @@ use super::function::DUMMY_BLOCK; #[derive(Debug, Clone, Copy)] pub(super) struct BlockNode<'a> { pub(super) func: &'a Function, + pub(super) cfg: &'a ControlFlowGraph, pub(super) block: Block, } impl<'a> BlockNode<'a> { - pub(super) fn new(func: &'a Function, block: Block) -> Self { - Self { func, block } + pub(super) fn new(func: &'a Function, cfg: &'a ControlFlowGraph, block: Block) -> Self { + Self { func, cfg, block } } pub(super) fn succs(self) -> Vec { let mut cfg = ControlFlowGraph::new(); cfg.compute(self.func); cfg.succs_of(self.block) - .map(|block| BlockNode::new(self.func, *block)) + .map(|block| BlockNode::new(self.func, self.cfg, *block)) .collect() } } impl<'a> BlockNode<'a> { pub(super) fn label(self) -> label::Text<'static> { - let Self { block, func } = self; + let Self { block, cfg, func } = self; let Function { sig, dfg, layout, .. } = func; @@ -50,7 +51,7 @@ impl<'a> BlockNode<'a> { // Write block body. write!(label, r#""#).unwrap(); for insn in layout.iter_insn(self.block) { - let display_insn = DisplayInsn::new(insn, dfg); + let display_insn = DisplayInsn::new(insn, func); let mut insn_string = String::new(); write!(&mut insn_string, "{}", display_insn).unwrap(); diff --git a/crates/ir/src/graphviz/function.rs b/crates/ir/src/graphviz/function.rs index c4db3f88..3954b5c2 100644 --- a/crates/ir/src/graphviz/function.rs +++ b/crates/ir/src/graphviz/function.rs @@ -8,18 +8,25 @@ use super::block::BlockNode; pub(super) const DUMMY_BLOCK: Block = Block(u32::MAX); -pub(super) struct FunctionGraph<'a>(pub(super) &'a Function); +pub(super) struct FunctionGraph<'a> { + func: &'a Function, + cfg: &'a ControlFlowGraph, +} + +impl<'a> FunctionGraph<'a> { + pub fn new(func: &'a Function, cfg: &'a ControlFlowGraph) -> Self { + Self { func, cfg } + } +} impl<'a> FunctionGraph<'a> { pub(super) fn blocks(&self) -> Vec> { - let Self(func) = *self; - let mut cfg = ControlFlowGraph::new(); - cfg.compute(func); + let Self { func, cfg } = self; // Dummy block is needed to label the graph with the function signature. Returns a vector // with the dummy block as a last element. cfg.post_order() - .map(|block| BlockNode::new(func, block)) - .chain(iter::once(BlockNode::new(func, DUMMY_BLOCK))) + .map(|block| BlockNode::new(func, cfg, block)) + .chain(iter::once(BlockNode::new(func, cfg, DUMMY_BLOCK))) .collect() } } @@ -30,7 +37,7 @@ impl<'a> Labeller<'a> for FunctionGraph<'a> { type Subgraph = (); fn graph_id(&self) -> dot2::Result> { - let Self(func) = *self; + let Self { func, cfg } = *self; let sig_name = func.sig.name().to_string(); Id::new(sig_name) } @@ -74,13 +81,13 @@ impl<'a> GraphWalk<'a> for FunctionGraph<'a> { } fn edges(&'a self) -> dot2::Edges<'a, Self::Edge> { - let Self(func) = *self; + let Self { func, cfg } = self; let mut blocks = self.blocks(); let dummy_block = blocks.pop().unwrap(); let mut edges = vec![BlockEdge { from: dummy_block, - to: BlockNode::new(func, Block(0u32)), + to: BlockNode::new(func, cfg, Block(0u32)), func, }]; for block in blocks { diff --git a/crates/ir/src/graphviz/mod.rs b/crates/ir/src/graphviz/mod.rs index 1070d78f..8f4fb272 100644 --- a/crates/ir/src/graphviz/mod.rs +++ b/crates/ir/src/graphviz/mod.rs @@ -1,6 +1,6 @@ use std::io; -use crate::Function; +use crate::{ControlFlowGraph, Function}; mod block; mod function; @@ -8,7 +8,9 @@ mod function; use function::FunctionGraph; pub fn render_to(func: &Function, output: &mut W) -> io::Result<()> { - let func_graph = FunctionGraph(func); + let mut cfg = ControlFlowGraph::new(); + cfg.compute(func); + let func_graph = FunctionGraph::new(func, &cfg); dot2::render(&func_graph, output).map_err(|err| match err { dot2::Error::Io(err) => err, _ => panic!("invalid graphviz id"), From 327e706d4125a66d3f4f5f2218bcee7dd0c5a698 Mon Sep 17 00:00:00 2001 From: Aki <140970520+k0aki@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:38:38 +0200 Subject: [PATCH 08/11] Fix instruction string representation Co-authored-by: Yoshitomo Nakanishi --- crates/ir/src/insn.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/ir/src/insn.rs b/crates/ir/src/insn.rs index 5b3e8804..ffc4ac43 100644 --- a/crates/ir/src/insn.rs +++ b/crates/ir/src/insn.rs @@ -440,34 +440,30 @@ impl<'a> fmt::Display for DisplayInsnData<'a> { match insn_data { Unary { code, args } => { let v = DisplayArgValues::new(args, dfg); - write!(f, "{} v{v}.;", code.as_str()) + write!(f, "{} {v};", code.as_str()) } Binary { code, args } => { let v = DisplayArgValues::new(args, dfg); write!(f, "{} {v};", code.as_str(),) } - Cast { code, args, ty } => { + Cast { code, args, .. } => { let v = DisplayArgValues::new(args, dfg); - let display_ty = DisplayType::new(*ty, dfg); - write!(f, "{} {v} {display_ty};", code.as_str()) + write!(f, "{} {v};", code.as_str()) } Load { args, loc } => { let v = DisplayArgValues::new(args, dfg); - write!(f, "load {v} {loc};") + write!(f, "{loc} load {v};") } Store { args, loc } => { let v = DisplayArgValues::new(args, dfg); - write!(f, "store {v} {loc};") + write!(f, "store {loc} {v};") } Call { - args, - func: callee, - ret_ty: ty, + args, func: callee, .. } => { let v = DisplayArgValues::new(args, dfg); let callee = DisplayCalleeFuncRef::new(callee, func); - let display_ty = DisplayType::new(*ty, dfg); - write!(f, "call %{callee} {v} {display_ty};") + write!(f, "call %{callee} {v};") } Jump { code, dests } => { let block = dests[0]; From 9aff428e15506d8ad474e9e3164c3d36a51ad4d5 Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 17:02:42 +0200 Subject: [PATCH 09/11] Avoid new assign --- crates/ir/src/insn.rs | 12 ++++++------ crates/ir/src/types.rs | 8 ++++---- crates/ir/src/value.rs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/ir/src/insn.rs b/crates/ir/src/insn.rs index ffc4ac43..c6f3c6de 100644 --- a/crates/ir/src/insn.rs +++ b/crates/ir/src/insn.rs @@ -36,11 +36,11 @@ impl<'a> fmt::Display for DisplayInsn<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let Self { insn, func } = *self; - let display_result = DisplayResultValue::new(insn, &func.dfg); - write!(f, "{display_result}")?; + let result = DisplayResultValue::new(insn, &func.dfg); + write!(f, "{result}")?; - let display_insn = DisplayInsnData::new(insn, func); - write!(f, "{display_insn}") + let insn = DisplayInsnData::new(insn, func); + write!(f, "{insn}") } } @@ -489,8 +489,8 @@ impl<'a> fmt::Display for DisplayInsnData<'a> { write!(f, " {};", table[table.len() - 1]) } Alloca { ty } => { - let display_ty = DisplayType::new(*ty, dfg); - write!(f, "alloca {display_ty};") + let ty = DisplayType::new(*ty, dfg); + write!(f, "alloca {ty};") } Return { args } => { write!(f, "ret")?; diff --git a/crates/ir/src/types.rs b/crates/ir/src/types.rs index 0cdff8e4..5bc3186c 100644 --- a/crates/ir/src/types.rs +++ b/crates/ir/src/types.rs @@ -151,12 +151,12 @@ impl<'a> fmt::Display for DisplayCompoundType<'a> { dfg.ctx .with_ty_store(|s| match s.resolve_compound(self.cmpd_ty) { Array { elem: ty, len } => { - let display_ty = DisplayType::new(*ty, dfg); - write!(f, "[{display_ty};{len}]") + let ty = DisplayType::new(*ty, dfg); + write!(f, "[{ty};{len}]") } Ptr(ty) => { - let display_ty = DisplayType::new(*ty, dfg); - write!(f, "*{display_ty}") + let ty = DisplayType::new(*ty, dfg); + write!(f, "*{ty}") } Struct(StructData { name, packed, .. }) => { if *packed { diff --git a/crates/ir/src/value.rs b/crates/ir/src/value.rs index 00656600..231764ab 100644 --- a/crates/ir/src/value.rs +++ b/crates/ir/src/value.rs @@ -27,8 +27,8 @@ impl<'a> fmt::Display for DisplayResultValue<'a> { let Self { insn, dfg } = *self; if let Some(value) = dfg.insn_result(insn) { let ty = dfg.insn_result_ty(insn).unwrap(); - let display_ty = DisplayType::new(ty, dfg); - return write!(f, "v{}.{display_ty} = ", value.0); + let ty = DisplayType::new(ty, dfg); + return write!(f, "v{}.{ty} = ", value.0); } Ok(()) } @@ -48,8 +48,8 @@ impl<'a, 'b> DisplayArgValues<'a, 'b> { let dfg = self.dfg; match *dfg.value_data(*arg) { ValueData::Immediate { imm, ty } => { - let display_ty = DisplayType::new(ty, dfg); - write!(w, "{imm}.{display_ty}") + let ty = DisplayType::new(ty, dfg); + write!(w, "{imm}.{ty}") } _ => write!(w, "v{}", arg.0), } From 819ca2bee761f578a9a6b3de537c2dec590b9e68 Mon Sep 17 00:00:00 2001 From: Aki Date: Mon, 14 Aug 2023 17:03:00 +0200 Subject: [PATCH 10/11] Fix clippy warnings --- crates/ir/src/graphviz/block.rs | 2 +- crates/ir/src/graphviz/function.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs index 314760e6..3957de39 100644 --- a/crates/ir/src/graphviz/block.rs +++ b/crates/ir/src/graphviz/block.rs @@ -29,7 +29,7 @@ impl<'a> BlockNode<'a> { impl<'a> BlockNode<'a> { pub(super) fn label(self) -> label::Text<'static> { - let Self { block, cfg, func } = self; + let Self { block, func, .. } = self; let Function { sig, dfg, layout, .. } = func; diff --git a/crates/ir/src/graphviz/function.rs b/crates/ir/src/graphviz/function.rs index 3954b5c2..2880504b 100644 --- a/crates/ir/src/graphviz/function.rs +++ b/crates/ir/src/graphviz/function.rs @@ -37,7 +37,7 @@ impl<'a> Labeller<'a> for FunctionGraph<'a> { type Subgraph = (); fn graph_id(&self) -> dot2::Result> { - let Self { func, cfg } = *self; + let func = self.func; let sig_name = func.sig.name().to_string(); Id::new(sig_name) } From e811a63b9f55127ef239cdd5f13c2005dd95f9ab Mon Sep 17 00:00:00 2001 From: Aki <140970520+k0aki@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:14:15 +0200 Subject: [PATCH 11/11] Fix commit 9a80406 Co-authored-by: Yoshitomo Nakanishi --- crates/ir/src/graphviz/block.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ir/src/graphviz/block.rs b/crates/ir/src/graphviz/block.rs index 3957de39..3a3335a9 100644 --- a/crates/ir/src/graphviz/block.rs +++ b/crates/ir/src/graphviz/block.rs @@ -19,9 +19,7 @@ impl<'a> BlockNode<'a> { } pub(super) fn succs(self) -> Vec { - let mut cfg = ControlFlowGraph::new(); - cfg.compute(self.func); - cfg.succs_of(self.block) + self.cfg.succs_of(self.block) .map(|block| BlockNode::new(self.func, self.cfg, *block)) .collect() }