From ba5468cc5cb4ff071c746e34dcdd331b84f37b0f Mon Sep 17 00:00:00 2001
From: rarbore2 <rarbore2@illinois.edu>
Date: Thu, 23 Jan 2025 11:55:47 -0600
Subject: [PATCH] Show basic blocks in Xdot

---
 hercules_cg/src/lib.rs   | 12 ------------
 hercules_ir/src/dot.rs   | 23 +++++++++++++++++++++++
 hercules_ir/src/ir.rs    | 12 ++++++++++++
 juno_scheduler/src/pm.rs | 13 +++++++++----
 4 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/hercules_cg/src/lib.rs b/hercules_cg/src/lib.rs
index a70effb5..8aaab214 100644
--- a/hercules_cg/src/lib.rs
+++ b/hercules_cg/src/lib.rs
@@ -10,18 +10,6 @@ pub use crate::rt::*;
 
 use hercules_ir::*;
 
-/*
- * Basic block info consists of two things:
- *
- * 1. A map from node to block (named by control nodes).
- * 2. For each node, which nodes are in its own block.
- *
- * Note that for #2, the structure is Vec<NodeID>, meaning the nodes are ordered
- * inside the block. This order corresponds to the traversal order of the nodes
- * in the block needed by the backend code generators.
- */
-pub type BasicBlocks = (Vec<NodeID>, Vec<Vec<NodeID>>);
-
 /*
  * The alignment of a type does not depend on dynamic constants.
  */
diff --git a/hercules_ir/src/dot.rs b/hercules_ir/src/dot.rs
index 4d526366..9dd2cb1e 100644
--- a/hercules_ir/src/dot.rs
+++ b/hercules_ir/src/dot.rs
@@ -18,6 +18,7 @@ pub fn xdot_module(
     reverse_postorders: &Vec<Vec<NodeID>>,
     doms: Option<&Vec<DomTree>>,
     fork_join_maps: Option<&Vec<HashMap<NodeID, NodeID>>>,
+    bbs: Option<&Vec<BasicBlocks>>,
 ) {
     let mut tmp_path = temp_dir();
     let mut rng = rand::thread_rng();
@@ -30,6 +31,7 @@ pub fn xdot_module(
         &reverse_postorders,
         doms,
         fork_join_maps,
+        bbs,
         &mut contents,
     )
     .expect("PANIC: Unable to generate output file contents.");
@@ -51,6 +53,7 @@ pub fn write_dot<W: Write>(
     reverse_postorders: &Vec<Vec<NodeID>>,
     doms: Option<&Vec<DomTree>>,
     fork_join_maps: Option<&Vec<HashMap<NodeID, NodeID>>>,
+    bbs: Option<&Vec<BasicBlocks>>,
     w: &mut W,
 ) -> std::fmt::Result {
     write_digraph_header(w)?;
@@ -165,6 +168,26 @@ pub fn write_dot<W: Write>(
             }
         }
 
+        // Step 4: draw basic block edges in indigo.
+        if let Some(bbs) = bbs {
+            let bbs = &bbs[function_id.idx()].0;
+            for (idx, bb) in bbs.into_iter().enumerate() {
+                if idx != bb.idx() {
+                    write_edge(
+                        NodeID::new(idx),
+                        function_id,
+                        *bb,
+                        function_id,
+                        true,
+                        "indigo",
+                        "dotted",
+                        &module,
+                        w,
+                    )?;
+                }
+            }
+        }
+
         write_graph_footer(w)?;
     }
 
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index d1fdd225..46d35f25 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -351,6 +351,18 @@ pub type FunctionSchedules = Vec<Vec<Schedule>>;
  */
 pub type FunctionLabels = Vec<HashSet<LabelID>>;
 
+/*
+ * Basic block info consists of two things:
+ *
+ * 1. A map from node to block (named by control nodes).
+ * 2. For each node, which nodes are in its own block.
+ *
+ * Note that for #2, the structure is Vec<NodeID>, meaning the nodes are ordered
+ * inside the block. This order corresponds to the traversal order of the nodes
+ * in the block needed by the backend code generators.
+ */
+pub type BasicBlocks = (Vec<NodeID>, Vec<Vec<NodeID>>);
+
 impl Module {
     /*
      * Printing out types, constants, and dynamic constants fully requires a
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index e93e7ecb..43fba4fd 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1529,15 +1529,15 @@ fn run_pass(
             pm.fork_join_maps = Some(fork_join_maps);
         }
         Pass::Xdot => {
-            assert!(args.len() == 1);
-            let force_analyses = match args[0] {
-                Value::Boolean { val } => val,
-                _ => {
+            let force_analyses = match args.get(0) {
+                Some(Value::Boolean { val }) => *val,
+                Some(_) => {
                     return Err(SchedulerError::PassError {
                         pass: "xdot".to_string(),
                         error: "expected boolean argument".to_string(),
                     });
                 }
+                None => true,
             };
 
             pm.make_reverse_postorders();
@@ -1549,14 +1549,19 @@ fn run_pass(
             let reverse_postorders = pm.reverse_postorders.take().unwrap();
             let doms = pm.doms.take();
             let fork_join_maps = pm.fork_join_maps.take();
+            let bbs = pm.bbs.take();
             pm.with_mod(|module| {
                 xdot_module(
                     module,
                     &reverse_postorders,
                     doms.as_ref(),
                     fork_join_maps.as_ref(),
+                    bbs.as_ref(),
                 )
             });
+
+            // Put BasicBlocks back, since it's needed for Codegen.
+            pm.bbs = bbs;
         }
     }
 
-- 
GitLab