From dbc6216ce62539d660fa1cd80e1a428ba93d1ee5 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Thu, 31 Oct 2024 16:07:10 -0500
Subject: [PATCH] Refactor PM, add pass to seralize module

---
 Cargo.lock                                 |  7 +++-
 hercules_ir/Cargo.toml                     |  2 +-
 hercules_ir/src/ir.rs                      | 18 +++++-----
 hercules_opt/src/pass.rs                   | 37 ++++++++++----------
 hercules_rt_proc/src/lib.rs                | 24 ++++++++-----
 hercules_samples/matmul/src/main.rs        |  2 +-
 hercules_tools/hercules_driver/Cargo.toml  |  1 +
 hercules_tools/hercules_driver/src/main.rs | 39 +++++++++++++++-------
 8 files changed, 81 insertions(+), 49 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 6ef91b76..27bf3c1e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "aho-corasick"
@@ -609,6 +609,7 @@ dependencies = [
  "clap",
  "hercules_ir",
  "hercules_opt",
+ "postcard",
  "ron",
 ]
 
@@ -950,6 +951,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
 dependencies = [
  "num-traits",
+ "rand",
+ "serde",
 ]
 
 [[package]]
@@ -1105,6 +1108,7 @@ dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
+ "serde",
 ]
 
 [[package]]
@@ -1124,6 +1128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
  "getrandom",
+ "serde",
 ]
 
 [[package]]
diff --git a/hercules_ir/Cargo.toml b/hercules_ir/Cargo.toml
index b99c0877..44648c11 100644
--- a/hercules_ir/Cargo.toml
+++ b/hercules_ir/Cargo.toml
@@ -6,6 +6,6 @@ authors = ["Russel Arbore <rarbore2@illinois.edu>, Aaron Councilman <aaronjc4@il
 [dependencies]
 rand = "*"
 nom = "*"
-ordered-float = "*"
+ordered-float = { version = "*", features = ["serde"] }
 bitvec = "*"
 serde = { version = "*", features = ["derive"] }
\ No newline at end of file
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 4c487c59..7b96f6b0 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -19,7 +19,7 @@ use crate::*;
  * wants to run an intraprocedural pass in parallel, it is advised to first
  * destruct the module, then reconstruct it once finished.
  */
-#[derive(Debug, Default, Clone)]
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
 pub struct Module {
     pub functions: Vec<Function>,
     pub types: Vec<Type>,
@@ -35,7 +35,7 @@ pub struct Module {
  * constants are 64-bit unsigned integers (usize / u64), so it is sufficient to
  * just store how many of them the function takes as arguments.
  */
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct Function {
     pub name: String,
     pub param_types: Vec<TypeID>,
@@ -77,7 +77,7 @@ pub enum Type {
  * interning constants during IR construction). Product, summation, and array
  * constants all contain their own type.
  */
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Constant {
     Boolean(bool),
     Integer8(i8),
@@ -128,7 +128,7 @@ pub enum DynamicConstant {
  * operate on an index list, composing indices at different levels in a type
  * tree. Each type that can be indexed has a unique variant in the index enum.
  */
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Index {
     Field(usize),
     Variant(usize),
@@ -146,7 +146,7 @@ pub enum Index {
  * side effects, so call nodes don't take as input or output control tokens.
  * There is also no global memory - use arrays.
  */
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Node {
     Start,
     Region {
@@ -232,14 +232,14 @@ pub enum Node {
     },
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum UnaryOperator {
     Not,
     Neg,
     Cast(TypeID),
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum BinaryOperator {
     Add,
     Sub,
@@ -259,12 +259,12 @@ pub enum BinaryOperator {
     RSh,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum TernaryOperator {
     Select,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Intrinsic {
     Abs,
     ACos,
diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs
index e96fb4b6..5866c57c 100644
--- a/hercules_opt/src/pass.rs
+++ b/hercules_opt/src/pass.rs
@@ -9,7 +9,6 @@ use std::env::temp_dir;
 use std::fs::File;
 use std::io::Write;
 use std::iter::zip;
-use std::path::Path;
 use std::process::{Command, Stdio};
 
 use self::serde::Deserialize;
@@ -37,7 +36,10 @@ pub enum Pass {
     // Useful to set to false if displaying a potentially broken module.
     Xdot(bool),
     SchedXdot,
-    Codegen,
+    // Parameterized over output directory and module name.
+    Codegen(String, String),
+    // Parameterized over where to serialize module to.
+    Serialize(String),
 }
 
 /*
@@ -321,7 +323,7 @@ impl PassManager {
         }
     }
 
-    pub fn run_passes(&mut self, input_file: &Path) {
+    pub fn run_passes(&mut self) {
         for pass in self.passes.clone().iter() {
             match pass {
                 Pass::DCE => {
@@ -536,7 +538,7 @@ impl PassManager {
 
                     xdot_sched_module(&smodule);
                 }
-                Pass::Codegen => {
+                Pass::Codegen(output_dir, module_name) => {
                     self.make_def_uses();
                     self.make_typing();
                     self.make_control_subgraphs();
@@ -568,44 +570,45 @@ impl PassManager {
                     println!("{}", llvm_ir);
 
                     // Write the LLVM IR into a temporary file.
-                    let output_file_prefix = input_file.file_stem().unwrap().to_str().unwrap();
                     let mut tmp_path = temp_dir();
-                    tmp_path.push(format!("{}.ll", output_file_prefix));
+                    tmp_path.push(format!("{}.ll", module_name));
                     let mut file = File::create(&tmp_path)
                         .expect("PANIC: Unable to open output LLVM IR file.");
                     file.write_all(llvm_ir.as_bytes())
                         .expect("PANIC: Unable to write output LLVM IR file contents.");
+                    println!("{}", tmp_path.display());
 
                     // Compile LLVM IR into an ELF object file.
+                    let output_archive = format!("{}/lib{}.a", output_dir, module_name);
                     let mut clang_process = Command::new("clang")
                         .arg(&tmp_path)
                         .arg("--emit-static-lib")
                         .arg("-O3")
                         .arg("-march=native")
                         .arg("-o")
-                        .arg({
-                            let mut lib_path = input_file.parent().unwrap().to_path_buf();
-                            lib_path.push(format!("lib{}.a", output_file_prefix));
-                            lib_path
-                        })
+                        .arg(&output_archive)
                         .stdin(Stdio::piped())
                         .stdout(Stdio::piped())
                         .spawn()
                         .unwrap();
                     assert!(clang_process.wait().unwrap().success());
+                    println!("{}", output_archive);
 
                     // Package manifest into a file.
                     let hman_contents: Vec<u8> = postcard::to_allocvec(&smodule.manifests).unwrap();
-                    let mut file = File::create({
-                        let mut hman_path = input_file.parent().unwrap().to_path_buf();
-                        hman_path.push(format!("{}.hman", output_file_prefix));
-                        hman_path
-                    })
-                    .expect("PANIC: Unable to open output manifest file.");
+                    let mut file = File::create(format!("{}/{}.hman", output_dir, module_name))
+                        .expect("PANIC: Unable to open output manifest file.");
                     file.write_all(&hman_contents)
                         .expect("PANIC: Unable to write output manifest file contents.");
                     self.manifests = Some(smodule.manifests);
                 }
+                Pass::Serialize(output_file) => {
+                    let module_contents: Vec<u8> = postcard::to_allocvec(&self.module).unwrap();
+                    let mut file = File::create(&output_file)
+                        .expect("PANIC: Unable to open output module file.");
+                    file.write_all(&module_contents)
+                        .expect("PANIC: Unable to write output module file contents.");
+                }
             }
         }
     }
diff --git a/hercules_rt_proc/src/lib.rs b/hercules_rt_proc/src/lib.rs
index dc9f8f6f..f3df33aa 100644
--- a/hercules_rt_proc/src/lib.rs
+++ b/hercules_rt_proc/src/lib.rs
@@ -116,7 +116,10 @@ fn compute_dynamic_constant<W: Write>(
 /*
  * Generate async Rust code orchestrating partition execution.
  */
-fn codegen(manifests: &HashMap<String, Manifest>, link_library: &Option<String>) -> Result<String, anyhow::Error> {
+fn codegen(
+    manifests: &HashMap<String, Manifest>,
+    link_library: &Option<String>,
+) -> Result<String, anyhow::Error> {
     // Write to a String containing all of the Rust code.
     let mut rust_code = "".to_string();
 
@@ -481,12 +484,17 @@ pub fn use_hman(path: TokenStream) -> TokenStream {
 
 #[proc_macro]
 pub fn use_hir(hir_tokens: TokenStream) -> TokenStream {
-    use TokenTree::Literal;
     use std::env;
+    use TokenTree::Literal;
 
     let mut tokens_iter = hir_tokens.into_iter();
-    let token = tokens_iter.next().expect("Please provide Hercules IR to use the use_hir! macro.");
-    assert!(tokens_iter.next().is_none(), "Too many tokens provided to use the use_hir! macro. Please provide only Hercules IR.");
+    let token = tokens_iter
+        .next()
+        .expect("Please provide Hercules IR to use the use_hir! macro.");
+    assert!(
+        tokens_iter.next().is_none(),
+        "Too many tokens provided to use the use_hir! macro. Please provide only Hercules IR."
+    );
     let literal = if let Literal(literal) = token {
         literal
     } else {
@@ -494,15 +502,15 @@ pub fn use_hir(hir_tokens: TokenStream) -> TokenStream {
     };
     let literal_string = literal.to_string();
 
-    let module =
-        hercules_ir::parse::parse(&literal_string[1..(literal_string.len() - 1)]).expect("PANIC: Failed to parse Hercules IR string.");
+    let module = hercules_ir::parse::parse(&literal_string[1..(literal_string.len() - 1)])
+        .expect("PANIC: Failed to parse Hercules IR string.");
     let out_dir = env::var("OUT_DIR").unwrap();
     let libname = format!("hir_generated_{}", uuid::Uuid::new_v4().simple());
 
     let mut p = hercules_opt::pass::PassManager::new(module);
-    p.add_pass(hercules_opt::pass::Pass::Codegen);
+    p.add_pass(hercules_opt::pass::Pass::Codegen(out_dir, libname.clone()));
 
-    p.run_passes(&std::path::Path::new(&out_dir).join(&libname));
+    p.run_passes();
 
     let manifests = p.get_manifests();
     let rust_code = codegen(&manifests, &Some(libname)).unwrap();
diff --git a/hercules_samples/matmul/src/main.rs b/hercules_samples/matmul/src/main.rs
index 9a84c6ef..1fcee18c 100644
--- a/hercules_samples/matmul/src/main.rs
+++ b/hercules_samples/matmul/src/main.rs
@@ -5,7 +5,7 @@ extern crate clap;
 extern crate hercules_rt;
 
 // To compile currently, run from the Hercules project root directory:
-// cargo run --bin hercules_driver hercules_samples/matmul/matmul.hir "Codegen"
+// cargo run --bin hercules_driver hercules_samples/matmul/matmul.hir "Codegen(\"hercules_samples/matmul\",\"matmul\")"
 // Then, you can execute this example with:
 // cargo run --bin hercules_matmul
 hercules_rt::use_hman!("hercules_samples/matmul/matmul.hman");
diff --git a/hercules_tools/hercules_driver/Cargo.toml b/hercules_tools/hercules_driver/Cargo.toml
index aa6d4f5e..9236c34a 100644
--- a/hercules_tools/hercules_driver/Cargo.toml
+++ b/hercules_tools/hercules_driver/Cargo.toml
@@ -6,5 +6,6 @@ authors = ["Russel Arbore <rarbore2@illinois.edu>"]
 [dependencies]
 clap = { version = "*", features = ["derive"] }
 ron = "*"
+postcard = { version = "*", features = ["alloc"] }
 hercules_ir = { path = "../../hercules_ir" }
 hercules_opt = { path = "../../hercules_opt" }
diff --git a/hercules_tools/hercules_driver/src/main.rs b/hercules_tools/hercules_driver/src/main.rs
index 70a7bafb..97c9fe2e 100644
--- a/hercules_tools/hercules_driver/src/main.rs
+++ b/hercules_tools/hercules_driver/src/main.rs
@@ -1,4 +1,5 @@
 extern crate clap;
+extern crate postcard;
 
 use std::fs::File;
 use std::io::prelude::*;
@@ -9,23 +10,37 @@ use clap::Parser;
 #[derive(Parser, Debug)]
 #[command(author, version, about, long_about = None)]
 struct Args {
-    hir_file: String,
+    file: String,
     passes: String,
 }
 
-fn main() {
-    let args = Args::parse();
-    if !args.hir_file.ends_with(".hir") {
-        eprintln!("WARNING: Running hercules_driver on a file without a .hir extension - interpreting as a textual Hercules IR file.");
-    }
-
-    let hir_path = Path::new(&args.hir_file);
-    let mut file = File::open(hir_path).expect("PANIC: Unable to open input file.");
+fn parse_file_from_hir(path: &Path) -> hercules_ir::ir::Module {
+    let mut file = File::open(path).expect("PANIC: Unable to open input file.");
     let mut contents = String::new();
     file.read_to_string(&mut contents)
         .expect("PANIC: Unable to read input file contents.");
-    let module =
-        hercules_ir::parse::parse(&contents).expect("PANIC: Failed to parse Hercules IR file.");
+    hercules_ir::parse::parse(&contents).expect("PANIC: Failed to parse Hercules IR file.")
+}
+
+fn parse_file_from_hbin(path: &Path) -> hercules_ir::ir::Module {
+    let mut file = File::open(path).expect("PANIC: Unable to open input file.");
+    let mut buffer = vec![];
+    file.read_to_end(&mut buffer).unwrap();
+    postcard::from_bytes(&buffer).unwrap()
+}
+
+fn main() {
+    let args = Args::parse();
+    assert!(
+        args.file.ends_with(".hir") || args.file.ends_with(".hbin"),
+        "PANIC: Running hercules_driver on a file without a .hir or .hbin extension."
+    );
+    let path = Path::new(&args.file);
+    let module = if args.file.ends_with(".hir") {
+        parse_file_from_hir(path)
+    } else {
+        parse_file_from_hbin(path)
+    };
 
     let mut pm = hercules_opt::pass::PassManager::new(module);
     let passes: Vec<hercules_opt::pass::Pass> = args
@@ -42,5 +57,5 @@ fn main() {
     for pass in passes {
         pm.add_pass(pass);
     }
-    pm.run_passes(hir_path);
+    pm.run_passes();
 }
-- 
GitLab