Skip to content
Snippets Groups Projects
Commit 4a778d81 authored by rarbore2's avatar rarbore2
Browse files

Refactor dot visualization

parent 59072d67
No related branches found
No related tags found
1 merge request!16Refactor dot visualization
...@@ -190,6 +190,7 @@ dependencies = [ ...@@ -190,6 +190,7 @@ dependencies = [
"bitvec", "bitvec",
"nom", "nom",
"ordered-float", "ordered-float",
"rand",
] ]
[[package]] [[package]]
......
...@@ -4,6 +4,7 @@ version = "0.1.0" ...@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Russel Arbore <rarbore2@illinois.edu>"] authors = ["Russel Arbore <rarbore2@illinois.edu>"]
[dependencies] [dependencies]
rand = "*"
nom = "*" nom = "*"
ordered-float = "*" ordered-float = "*"
bitvec = "*" bitvec = "*"
\ No newline at end of file
extern crate hercules_ir; extern crate rand;
use std::collections::HashMap; use std::collections::HashMap;
use std::env::temp_dir;
use std::fmt::Write; use std::fmt::Write;
use std::fs::File;
use std::io::Write as _;
use std::process::Command;
use self::hercules_ir::*; use self::rand::Rng;
use crate::*;
/*
* Top level function to compute a dot graph for a module, and immediately
* render it using xdot.
*/
pub fn xdot_module(
module: &ir::Module,
reverse_postorders: &Vec<Vec<NodeID>>,
doms: Option<&Vec<DomTree>>,
fork_join_maps: Option<&Vec<HashMap<NodeID, NodeID>>>,
plans: Option<&Vec<Plan>>,
) {
let mut tmp_path = temp_dir();
let mut rng = rand::thread_rng();
let num: u64 = rng.gen();
tmp_path.push(format!("hercules_dot_{}.dot", num));
let mut file = File::create(tmp_path.clone()).expect("PANIC: Unable to open output file.");
let mut contents = String::new();
write_dot(
&module,
&reverse_postorders,
doms,
fork_join_maps,
plans,
&mut contents,
)
.expect("PANIC: Unable to generate output file contents.");
file.write_all(contents.as_bytes())
.expect("PANIC: Unable to write output file contents.");
Command::new("xdot")
.args([tmp_path])
.output()
.expect("PANIC: Couldn't execute xdot.");
}
/* /*
* Top level function to write a module out as a dot graph. Takes references to * Top level function to write a module out as a dot graph. Optionally takes
* many analysis results to generate a more informative dot graph. * references to many analysis results to generate a more informative dot graph.
*/ */
pub fn write_dot<W: Write>( pub fn write_dot<W: Write>(
module: &ir::Module, module: &ir::Module,
reverse_postorders: &Vec<Vec<NodeID>>, reverse_postorders: &Vec<Vec<NodeID>>,
typing: &ModuleTyping, doms: Option<&Vec<DomTree>>,
doms: &Vec<DomTree>, fork_join_maps: Option<&Vec<HashMap<NodeID, NodeID>>>,
fork_join_maps: &Vec<HashMap<NodeID, NodeID>>, plans: Option<&Vec<Plan>>,
plans: &Vec<Plan>,
w: &mut W, w: &mut W,
) -> std::fmt::Result { ) -> std::fmt::Result {
write_digraph_header(w)?; write_digraph_header(w)?;
...@@ -23,28 +62,38 @@ pub fn write_dot<W: Write>( ...@@ -23,28 +62,38 @@ pub fn write_dot<W: Write>(
for function_id in (0..module.functions.len()).map(FunctionID::new) { for function_id in (0..module.functions.len()).map(FunctionID::new) {
let function = &module.functions[function_id.idx()]; let function = &module.functions[function_id.idx()];
let reverse_postorder = &reverse_postorders[function_id.idx()]; let reverse_postorder = &reverse_postorders[function_id.idx()];
let plan = &plans[function_id.idx()]; let plan = plans.map(|plans| &plans[function_id.idx()]);
let mut reverse_postorder_node_numbers = vec![0; function.nodes.len()]; let mut reverse_postorder_node_numbers = vec![0; function.nodes.len()];
for (idx, id) in reverse_postorder.iter().enumerate() { for (idx, id) in reverse_postorder.iter().enumerate() {
reverse_postorder_node_numbers[id.idx()] = idx; reverse_postorder_node_numbers[id.idx()] = idx;
} }
write_subgraph_header(function_id, module, w)?; write_subgraph_header(function_id, module, w)?;
let partition_to_node_map = plan.invert_partition_map(); let mut partition_to_node_map = plan.map(|plan| plan.invert_partition_map());
// Step 1: draw IR graph itself. This includes all IR nodes and all edges // Step 1: draw IR graph itself. This includes all IR nodes and all edges
// between IR nodes. // between IR nodes.
for partition_idx in 0..plan.num_partitions { for partition_idx in 0..plan.map_or(1, |plan| plan.num_partitions) {
let partition_color = match plan.partition_devices[partition_idx] { let partition_color = plan.map(|plan| match plan.partition_devices[partition_idx] {
Device::CPU => "lightblue", Device::CPU => "lightblue",
Device::GPU => "darkseagreen", Device::GPU => "darkseagreen",
});
if let Some(partition_color) = partition_color {
write_partition_header(function_id, partition_idx, module, partition_color, w)?;
}
let nodes_ids = if let Some(partition_to_node_map) = &mut partition_to_node_map {
let mut empty = vec![];
std::mem::swap(&mut partition_to_node_map[partition_idx], &mut empty);
empty
} else {
(0..function.nodes.len())
.map(NodeID::new)
.collect::<Vec<_>>()
}; };
write_partition_header(function_id, partition_idx, module, partition_color, w)?; for node_id in nodes_ids.iter() {
for node_id in &partition_to_node_map[partition_idx] {
let node = &function.nodes[node_id.idx()]; let node = &function.nodes[node_id.idx()];
let dst_ty = &module.types[typing[function_id.idx()][node_id.idx()].idx()]; let dst_control = node.is_control();
let dst_strictly_control = node.is_strictly_control();
let dst_control = dst_ty.is_control() || dst_strictly_control;
// Control nodes are dark red, data nodes are dark blue. // Control nodes are dark red, data nodes are dark blue.
let color = if dst_control { "darkred" } else { "darkblue" }; let color = if dst_control { "darkred" } else { "darkblue" };
...@@ -54,14 +103,12 @@ pub fn write_dot<W: Write>( ...@@ -54,14 +103,12 @@ pub fn write_dot<W: Write>(
function_id, function_id,
color, color,
module, module,
&plan.schedules[node_id.idx()], plan.map_or(&vec![], |plan| &plan.schedules[node_id.idx()]),
w, w,
)?; )?;
for u in def_use::get_uses(&node).as_ref() { for u in def_use::get_uses(&node).as_ref() {
let src_ty = &module.types[typing[function_id.idx()][u.idx()].idx()]; let src_control = function.nodes[u.idx()].is_control();
let src_strictly_control = function.nodes[u.idx()].is_strictly_control();
let src_control = src_ty.is_control() || src_strictly_control;
// An edge between control nodes is dashed. An edge between data // An edge between control nodes is dashed. An edge between data
// nodes is filled. An edge between a control node and a data // nodes is filled. An edge between a control node and a data
...@@ -101,40 +148,46 @@ pub fn write_dot<W: Write>( ...@@ -101,40 +148,46 @@ pub fn write_dot<W: Write>(
)?; )?;
} }
} }
write_graph_footer(w)?; if plans.is_some() {
write_graph_footer(w)?;
}
} }
// Step 2: draw dominance edges in dark green. Don't draw post dominance // Step 2: draw dominance edges in dark green. Don't draw post dominance
// edges because then xdot lays out the graph strangely. // edges because then xdot lays out the graph strangely.
let dom = &doms[function_id.idx()]; if let Some(doms) = doms {
for (child_id, (_, parent_id)) in dom.get_underlying_map() { let dom = &doms[function_id.idx()];
write_edge( for (child_id, (_, parent_id)) in dom.get_underlying_map() {
*child_id, write_edge(
function_id, *child_id,
*parent_id, function_id,
function_id, *parent_id,
true, function_id,
"darkgreen", true,
"dotted", "darkgreen",
&module, "dotted",
w, &module,
)?; w,
)?;
}
} }
// Step 3: draw fork join edges in dark magenta. // Step 3: draw fork join edges in dark magenta.
let fork_join_map = &fork_join_maps[function_id.idx()]; if let Some(fork_join_maps) = fork_join_maps {
for (fork_id, join_id) in fork_join_map { let fork_join_map = &fork_join_maps[function_id.idx()];
write_edge( for (fork_id, join_id) in fork_join_map {
*join_id, write_edge(
function_id, *join_id,
*fork_id, function_id,
function_id, *fork_id,
true, function_id,
"darkmagenta", true,
"dotted", "darkmagenta",
&module, "dotted",
w, &module,
)?; w,
)?;
}
} }
write_graph_footer(w)?; write_graph_footer(w)?;
......
...@@ -4,6 +4,7 @@ pub mod build; ...@@ -4,6 +4,7 @@ pub mod build;
pub mod dataflow; pub mod dataflow;
pub mod def_use; pub mod def_use;
pub mod dom; pub mod dom;
pub mod dot;
pub mod ir; pub mod ir;
pub mod loops; pub mod loops;
pub mod parse; pub mod parse;
...@@ -16,6 +17,7 @@ pub use crate::build::*; ...@@ -16,6 +17,7 @@ pub use crate::build::*;
pub use crate::dataflow::*; pub use crate::dataflow::*;
pub use crate::def_use::*; pub use crate::def_use::*;
pub use crate::dom::*; pub use crate::dom::*;
pub use crate::dot::*;
pub use crate::ir::*; pub use crate::ir::*;
pub use crate::loops::*; pub use crate::loops::*;
pub use crate::parse::*; pub use crate::parse::*;
......
...@@ -6,6 +6,7 @@ use std::iter::zip; ...@@ -6,6 +6,7 @@ use std::iter::zip;
use self::hercules_ir::dataflow::*; use self::hercules_ir::dataflow::*;
use self::hercules_ir::def_use::*; use self::hercules_ir::def_use::*;
use self::hercules_ir::dom::*; use self::hercules_ir::dom::*;
use self::hercules_ir::dot::*;
use self::hercules_ir::ir::*; use self::hercules_ir::ir::*;
use self::hercules_ir::loops::*; use self::hercules_ir::loops::*;
use self::hercules_ir::subgraph::*; use self::hercules_ir::subgraph::*;
...@@ -24,6 +25,7 @@ pub enum Pass { ...@@ -24,6 +25,7 @@ pub enum Pass {
GVN, GVN,
Forkify, Forkify,
Verify, Verify,
Xdot,
} }
/* /*
...@@ -242,6 +244,16 @@ impl PassManager { ...@@ -242,6 +244,16 @@ impl PassManager {
// Verification doesn't require clearing analysis results. // Verification doesn't require clearing analysis results.
continue; continue;
} }
Pass::Xdot => {
self.make_reverse_postorders();
xdot_module(
&self.module,
self.reverse_postorders.as_ref().unwrap(),
self.doms.as_ref(),
self.fork_join_maps.as_ref(),
None,
);
}
} }
for idx in 0..self.module.functions.len() { for idx in 0..self.module.functions.len() {
......
...@@ -33,6 +33,8 @@ fn main() { ...@@ -33,6 +33,8 @@ fn main() {
pm.add_pass(hercules_opt::pass::Pass::DCE); pm.add_pass(hercules_opt::pass::Pass::DCE);
pm.add_pass(hercules_opt::pass::Pass::GVN); pm.add_pass(hercules_opt::pass::Pass::GVN);
pm.add_pass(hercules_opt::pass::Pass::DCE); pm.add_pass(hercules_opt::pass::Pass::DCE);
pm.add_pass(hercules_opt::pass::Pass::Forkify);
pm.add_pass(hercules_opt::pass::Pass::DCE);
let mut module = pm.run_passes(); let mut module = pm.run_passes();
let (def_uses, reverse_postorders, typing, subgraphs, doms, _postdoms, fork_join_maps) = let (def_uses, reverse_postorders, typing, subgraphs, doms, _postdoms, fork_join_maps) =
......
extern crate clap; extern crate clap;
extern crate rand; extern crate rand;
use std::env::temp_dir;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::process::Command;
use clap::Parser; use clap::Parser;
use rand::Rng;
pub mod dot;
use dot::*;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
...@@ -76,38 +69,22 @@ fn main() { ...@@ -76,38 +69,22 @@ fn main() {
.collect(); .collect();
if args.output.is_empty() { if args.output.is_empty() {
let mut tmp_path = temp_dir(); hercules_ir::dot::xdot_module(
let mut rng = rand::thread_rng();
let num: u64 = rng.gen();
tmp_path.push(format!("hercules_dot_{}.dot", num));
let mut file = File::create(tmp_path.clone()).expect("PANIC: Unable to open output file.");
let mut contents = String::new();
write_dot(
&module, &module,
&reverse_postorders, &reverse_postorders,
&typing, Some(&doms),
&doms, Some(&fork_join_maps),
&fork_join_maps, Some(&plans),
&plans, );
&mut contents,
)
.expect("PANIC: Unable to generate output file contents.");
file.write_all(contents.as_bytes())
.expect("PANIC: Unable to write output file contents.");
Command::new("xdot")
.args([tmp_path])
.output()
.expect("PANIC: Couldn't execute xdot.");
} else { } else {
let mut file = File::create(args.output).expect("PANIC: Unable to open output file."); let mut file = File::create(args.output).expect("PANIC: Unable to open output file.");
let mut contents = String::new(); let mut contents = String::new();
write_dot( hercules_ir::dot::write_dot(
&module, &module,
&reverse_postorders, &reverse_postorders,
&typing, Some(&doms),
&doms, Some(&fork_join_maps),
&fork_join_maps, Some(&plans),
&plans,
&mut contents, &mut contents,
) )
.expect("PANIC: Unable to generate output file contents."); .expect("PANIC: Unable to generate output file contents.");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment