From 4a778d810207310e2376c08a600f392007587069 Mon Sep 17 00:00:00 2001 From: rarbore2 <rarbore2@illinois.edu> Date: Wed, 28 Feb 2024 16:07:15 -0600 Subject: [PATCH] Refactor dot visualization --- Cargo.lock | 1 + hercules_ir/Cargo.toml | 1 + .../hercules_dot => hercules_ir}/src/dot.rs | 149 ++++++++++++------ hercules_ir/src/lib.rs | 2 + hercules_opt/src/pass.rs | 12 ++ hercules_tools/hercules_cpu/src/main.rs | 2 + hercules_tools/hercules_dot/src/main.rs | 41 ++--- 7 files changed, 128 insertions(+), 80 deletions(-) rename {hercules_tools/hercules_dot => hercules_ir}/src/dot.rs (71%) diff --git a/Cargo.lock b/Cargo.lock index 2fb56d26..a3d1a27a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,6 +190,7 @@ dependencies = [ "bitvec", "nom", "ordered-float", + "rand", ] [[package]] diff --git a/hercules_ir/Cargo.toml b/hercules_ir/Cargo.toml index aab636b2..b68e6b24 100644 --- a/hercules_ir/Cargo.toml +++ b/hercules_ir/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Russel Arbore <rarbore2@illinois.edu>"] [dependencies] +rand = "*" nom = "*" ordered-float = "*" bitvec = "*" \ No newline at end of file diff --git a/hercules_tools/hercules_dot/src/dot.rs b/hercules_ir/src/dot.rs similarity index 71% rename from hercules_tools/hercules_dot/src/dot.rs rename to hercules_ir/src/dot.rs index 751bd7c8..63053842 100644 --- a/hercules_tools/hercules_dot/src/dot.rs +++ b/hercules_ir/src/dot.rs @@ -1,21 +1,60 @@ -extern crate hercules_ir; +extern crate rand; use std::collections::HashMap; +use std::env::temp_dir; 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 - * many analysis results to generate a more informative dot graph. + * Top level function to write a module out as a dot graph. Optionally takes + * references to many analysis results to generate a more informative dot graph. */ pub fn write_dot<W: Write>( module: &ir::Module, reverse_postorders: &Vec<Vec<NodeID>>, - typing: &ModuleTyping, - doms: &Vec<DomTree>, - fork_join_maps: &Vec<HashMap<NodeID, NodeID>>, - plans: &Vec<Plan>, + doms: Option<&Vec<DomTree>>, + fork_join_maps: Option<&Vec<HashMap<NodeID, NodeID>>>, + plans: Option<&Vec<Plan>>, w: &mut W, ) -> std::fmt::Result { write_digraph_header(w)?; @@ -23,28 +62,38 @@ pub fn write_dot<W: Write>( for function_id in (0..module.functions.len()).map(FunctionID::new) { let function = &module.functions[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()]; for (idx, id) in reverse_postorder.iter().enumerate() { reverse_postorder_node_numbers[id.idx()] = idx; } 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 // between IR nodes. - for partition_idx in 0..plan.num_partitions { - let partition_color = match plan.partition_devices[partition_idx] { + for partition_idx in 0..plan.map_or(1, |plan| plan.num_partitions) { + let partition_color = plan.map(|plan| match plan.partition_devices[partition_idx] { Device::CPU => "lightblue", 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 &partition_to_node_map[partition_idx] { + for node_id in nodes_ids.iter() { let node = &function.nodes[node_id.idx()]; - let dst_ty = &module.types[typing[function_id.idx()][node_id.idx()].idx()]; - let dst_strictly_control = node.is_strictly_control(); - let dst_control = dst_ty.is_control() || dst_strictly_control; + let dst_control = node.is_control(); // Control nodes are dark red, data nodes are dark blue. let color = if dst_control { "darkred" } else { "darkblue" }; @@ -54,14 +103,12 @@ pub fn write_dot<W: Write>( function_id, color, module, - &plan.schedules[node_id.idx()], + plan.map_or(&vec![], |plan| &plan.schedules[node_id.idx()]), w, )?; for u in def_use::get_uses(&node).as_ref() { - let src_ty = &module.types[typing[function_id.idx()][u.idx()].idx()]; - let src_strictly_control = function.nodes[u.idx()].is_strictly_control(); - let src_control = src_ty.is_control() || src_strictly_control; + let src_control = function.nodes[u.idx()].is_control(); // An edge between control nodes is dashed. An edge between data // nodes is filled. An edge between a control node and a data @@ -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 // edges because then xdot lays out the graph strangely. - let dom = &doms[function_id.idx()]; - for (child_id, (_, parent_id)) in dom.get_underlying_map() { - write_edge( - *child_id, - function_id, - *parent_id, - function_id, - true, - "darkgreen", - "dotted", - &module, - w, - )?; + if let Some(doms) = doms { + let dom = &doms[function_id.idx()]; + for (child_id, (_, parent_id)) in dom.get_underlying_map() { + write_edge( + *child_id, + function_id, + *parent_id, + function_id, + true, + "darkgreen", + "dotted", + &module, + w, + )?; + } } // Step 3: draw fork join edges in dark magenta. - let fork_join_map = &fork_join_maps[function_id.idx()]; - for (fork_id, join_id) in fork_join_map { - write_edge( - *join_id, - function_id, - *fork_id, - function_id, - true, - "darkmagenta", - "dotted", - &module, - w, - )?; + if let Some(fork_join_maps) = fork_join_maps { + let fork_join_map = &fork_join_maps[function_id.idx()]; + for (fork_id, join_id) in fork_join_map { + write_edge( + *join_id, + function_id, + *fork_id, + function_id, + true, + "darkmagenta", + "dotted", + &module, + w, + )?; + } } write_graph_footer(w)?; diff --git a/hercules_ir/src/lib.rs b/hercules_ir/src/lib.rs index 2b8bac06..f402a788 100644 --- a/hercules_ir/src/lib.rs +++ b/hercules_ir/src/lib.rs @@ -4,6 +4,7 @@ pub mod build; pub mod dataflow; pub mod def_use; pub mod dom; +pub mod dot; pub mod ir; pub mod loops; pub mod parse; @@ -16,6 +17,7 @@ pub use crate::build::*; pub use crate::dataflow::*; pub use crate::def_use::*; pub use crate::dom::*; +pub use crate::dot::*; pub use crate::ir::*; pub use crate::loops::*; pub use crate::parse::*; diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs index 713a9c4a..fcb0e511 100644 --- a/hercules_opt/src/pass.rs +++ b/hercules_opt/src/pass.rs @@ -6,6 +6,7 @@ use std::iter::zip; use self::hercules_ir::dataflow::*; use self::hercules_ir::def_use::*; use self::hercules_ir::dom::*; +use self::hercules_ir::dot::*; use self::hercules_ir::ir::*; use self::hercules_ir::loops::*; use self::hercules_ir::subgraph::*; @@ -24,6 +25,7 @@ pub enum Pass { GVN, Forkify, Verify, + Xdot, } /* @@ -242,6 +244,16 @@ impl PassManager { // Verification doesn't require clearing analysis results. 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() { diff --git a/hercules_tools/hercules_cpu/src/main.rs b/hercules_tools/hercules_cpu/src/main.rs index 6a5d2bd2..32c4e064 100644 --- a/hercules_tools/hercules_cpu/src/main.rs +++ b/hercules_tools/hercules_cpu/src/main.rs @@ -33,6 +33,8 @@ fn main() { pm.add_pass(hercules_opt::pass::Pass::DCE); pm.add_pass(hercules_opt::pass::Pass::GVN); 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 (def_uses, reverse_postorders, typing, subgraphs, doms, _postdoms, fork_join_maps) = diff --git a/hercules_tools/hercules_dot/src/main.rs b/hercules_tools/hercules_dot/src/main.rs index 39f2057a..dfe8db49 100644 --- a/hercules_tools/hercules_dot/src/main.rs +++ b/hercules_tools/hercules_dot/src/main.rs @@ -1,18 +1,11 @@ extern crate clap; extern crate rand; -use std::env::temp_dir; use std::fs::File; use std::io::prelude::*; -use std::process::Command; use clap::Parser; -use rand::Rng; - -pub mod dot; -use dot::*; - #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { @@ -76,38 +69,22 @@ fn main() { .collect(); if args.output.is_empty() { - 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( + hercules_ir::dot::xdot_module( &module, &reverse_postorders, - &typing, - &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."); + Some(&doms), + Some(&fork_join_maps), + Some(&plans), + ); } else { let mut file = File::create(args.output).expect("PANIC: Unable to open output file."); let mut contents = String::new(); - write_dot( + hercules_ir::dot::write_dot( &module, &reverse_postorders, - &typing, - &doms, - &fork_join_maps, - &plans, + Some(&doms), + Some(&fork_join_maps), + Some(&plans), &mut contents, ) .expect("PANIC: Unable to generate output file contents."); -- GitLab