From a1baea161e0114374de969618cec803a56e42ea0 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Wed, 12 Feb 2025 17:08:36 -0600
Subject: [PATCH] Loop unroll skeleton

---
 hercules_opt/src/lib.rs       |  2 ++
 hercules_opt/src/unroll.rs    | 18 ++++++++++++++++++
 juno_scheduler/src/compile.rs |  1 +
 juno_scheduler/src/ir.rs      |  1 +
 juno_scheduler/src/pm.rs      | 18 ++++++++++++++++++
 5 files changed, 40 insertions(+)
 create mode 100644 hercules_opt/src/unroll.rs

diff --git a/hercules_opt/src/lib.rs b/hercules_opt/src/lib.rs
index 7187508a..a810dfbf 100644
--- a/hercules_opt/src/lib.rs
+++ b/hercules_opt/src/lib.rs
@@ -23,6 +23,7 @@ pub mod simplify_cfg;
 pub mod slf;
 pub mod sroa;
 pub mod unforkify;
+pub mod unroll;
 pub mod utils;
 
 pub use crate::ccp::*;
@@ -48,4 +49,5 @@ pub use crate::simplify_cfg::*;
 pub use crate::slf::*;
 pub use crate::sroa::*;
 pub use crate::unforkify::*;
+pub use crate::unroll::*;
 pub use crate::utils::*;
diff --git a/hercules_opt/src/unroll.rs b/hercules_opt/src/unroll.rs
new file mode 100644
index 00000000..f3c795ca
--- /dev/null
+++ b/hercules_opt/src/unroll.rs
@@ -0,0 +1,18 @@
+use bitvec::prelude::*;
+
+use hercules_ir::*;
+
+use crate::*;
+
+/*
+ * Run loop unrolling on all loops that are mutable in an editor.
+ */
+pub fn loop_unroll_all_loops(editor: &mut FunctionEditor, loops: &LoopTree) {
+    for (header, contents) in loops.bottom_up_loops() {
+        if editor.is_mutable(header) {
+            loop_unroll(editor, header, contents);
+        }
+    }
+}
+
+pub fn loop_unroll(editor: &mut FunctionEditor, header: NodeID, contents: &BitVec<u8, Lsb0>) {}
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 1aaa10cd..9d5a86cc 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -114,6 +114,7 @@ impl FromStr for Appliable {
             "fork-interchange" => Ok(Appliable::Pass(ir::Pass::ForkInterchange)),
             "fork-chunk" | "fork-tile" => Ok(Appliable::Pass(ir::Pass::ForkChunk)),
             "lift-dc-math" => Ok(Appliable::Pass(ir::Pass::LiftDCMath)),
+            "loop-unroll" | "unroll" => Ok(Appliable::Pass(ir::Pass::LoopUnroll)),
             "outline" => Ok(Appliable::Pass(ir::Pass::Outline)),
             "phi-elim" => Ok(Appliable::Pass(ir::Pass::PhiElim)),
             "predication" => Ok(Appliable::Pass(ir::Pass::Predication)),
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 0ecac39a..1bb6cf13 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -23,6 +23,7 @@ pub enum Pass {
     Inline,
     InterproceduralSROA,
     LiftDCMath,
+    LoopUnroll,
     Outline,
     PhiElim,
     Predication,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 9c7391ac..5c6aec5e 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1665,6 +1665,24 @@ fn run_pass(
             pm.delete_gravestones();
             pm.clear_analyses();
         }
+        Pass::LoopUnroll => {
+            assert_eq!(args.len(), 0);
+
+            pm.make_loops();
+            let loops = pm.loops.take().unwrap();
+            for (func, loops) in build_selection(pm, selection, false)
+                .into_iter()
+                .zip(loops.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                loop_unroll_all_loops(&mut func, loops);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
         Pass::Forkify => {
             assert!(args.is_empty());
             loop {
-- 
GitLab