From 2d3666786d294b9b1eefb74811e59bf3e1fd0028 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 10:10:36 -0600
Subject: [PATCH 01/46] Design new scheduler IR

---
 juno_scheduler/src/ir.rs  | 72 +++++++++++++++++++++++++++++++++++++++
 juno_scheduler/src/lib.rs |  6 ++++
 juno_scheduler/src/pm.rs  | 54 +++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+)
 create mode 100644 juno_scheduler/src/ir.rs
 create mode 100644 juno_scheduler/src/pm.rs

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
new file mode 100644
index 00000000..237e4f1a
--- /dev/null
+++ b/juno_scheduler/src/ir.rs
@@ -0,0 +1,72 @@
+extern crate hercules_ir;
+
+use self::hercules_ir::ir::{FunctionSchedule, Schedule};
+
+#[derive(Debug, Clone)]
+pub enum Pass {
+    DCE,
+    CCP,
+    GVN,
+    PhiElim,
+    Forkify,
+    ForkGuardElim,
+    Predication,
+    SROA,
+    Inline,
+    Outline,
+    InterproceduralSROA,
+    DeleteUncalled,
+    ForkSplit,
+    Unforkify,
+    InferSchedules,
+}
+
+#[derive(Debug, Clone)]
+pub enum Selector {
+    Everything(),
+    Selection(Vec<ScheduleExp>),
+}
+
+#[derive(Debug, Clone)]
+pub enum ScheduleExp {
+    Variable {
+        var: String,
+    },
+    Field {
+        collect: Box<ScheduleExp>,
+        field: String,
+    },
+    RunPass {
+        pass: Pass,
+        on: Selector,
+    },
+    Record {
+        fields: Vec<(String, ScheduleExp)>,
+    },
+    Block {
+        body: Vec<ScheduleStmt>,
+        res: Box<ScheduleExp>,
+    },
+}
+
+#[derive(Debug, Clone)]
+pub enum ScheduleStmt {
+    Fixpoint {
+        body: Box<ScheduleStmt>,
+    },
+    Block {
+        body: Vec<ScheduleStmt>,
+    },
+    Assign {
+        var: String,
+        exp: ScheduleExp,
+    },
+    AddSchedule {
+        sched: Schedule,
+        on: Selector,
+    },
+    AddDevice {
+        device: FunctionSchedule,
+        on: Selector,
+    },
+}
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 7e558d6b..8790e537 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -12,6 +12,12 @@ use self::hercules_ir::ir::*;
 mod parser;
 use crate::parser::lexer;
 
+mod ir;
+mod pm;
+
+use crate::ir::*;
+use crate::pm::*;
+
 // FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
 // - The map from label names to their numbers
 // - The name of the function
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
new file mode 100644
index 00000000..a0562083
--- /dev/null
+++ b/juno_scheduler/src/pm.rs
@@ -0,0 +1,54 @@
+use crate::ir::*;
+use hercules_ir::{FunctionID, NodeID, ID};
+
+use std::collections::HashMap;
+
+#[derive(Debug, Clone)]
+pub enum LabelInfo {
+    Loop(NodeID),
+    Fork(NodeID),
+    Block(Vec<NodeID>),
+}
+
+#[derive(Debug, Clone)]
+pub struct ProgramLabels {
+    labels: Vec<Vec<LabelInfo>>,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct LabelID {
+    func: FunctionID,
+    label: usize,
+}
+
+impl ProgramLabels {
+    pub fn get_label(&self, label: LabelID) -> &LabelInfo {
+        &self.labels[label.func.idx()][label.label]
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct JunoFunctions {
+    functions: Vec<HashMap<FunctionID, HashMap<String, LabelID>>>,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct JunoFunctionID {
+    idx: usize,
+}
+
+impl JunoFunctions {
+    pub fn get_function(
+        &self,
+        func: JunoFunctionID,
+    ) -> &HashMap<FunctionID, HashMap<String, LabelID>> {
+        &self.functions[func.idx]
+    }
+}
+
+#[derive(Debug, Clone)]
+pub enum Value {
+    Label { label: LabelID },
+    Function { func: JunoFunctionID },
+    Record { fields: HashMap<String, Value> },
+}
-- 
GitLab


From 2a54e9c90a2489a96071c36c39b87d506a5eb887 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 10:22:24 -0600
Subject: [PATCH 02/46] Reorganize scheduler inputs

---
 juno_scheduler/src/labels.rs | 47 ++++++++++++++++++++++++++++++++++++
 juno_scheduler/src/lib.rs    |  2 ++
 juno_scheduler/src/pm.rs     | 44 +--------------------------------
 3 files changed, 50 insertions(+), 43 deletions(-)
 create mode 100644 juno_scheduler/src/labels.rs

diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
new file mode 100644
index 00000000..85552789
--- /dev/null
+++ b/juno_scheduler/src/labels.rs
@@ -0,0 +1,47 @@
+use crate::ir::*;
+use hercules_ir::{FunctionID, NodeID, ID};
+
+use std::collections::HashMap;
+
+#[derive(Debug, Clone)]
+pub enum LabelInfo {
+    Loop(NodeID),
+    Fork(NodeID),
+    Block(Vec<NodeID>),
+}
+
+#[derive(Debug, Clone)]
+pub struct ProgramLabels {
+    labels: Vec<Vec<LabelInfo>>,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct LabelID {
+    func: FunctionID,
+    label: usize,
+}
+
+impl ProgramLabels {
+    pub fn get_label(&self, label: LabelID) -> &LabelInfo {
+        &self.labels[label.func.idx()][label.label]
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct JunoFunctions {
+    functions: Vec<HashMap<FunctionID, HashMap<String, LabelID>>>,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct JunoFunctionID {
+    idx: usize,
+}
+
+impl JunoFunctions {
+    pub fn get_function(
+        &self,
+        func: JunoFunctionID,
+    ) -> &HashMap<FunctionID, HashMap<String, LabelID>> {
+        &self.functions[func.idx]
+    }
+}
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 8790e537..d0eafac3 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -13,9 +13,11 @@ mod parser;
 use crate::parser::lexer;
 
 mod ir;
+mod labels;
 mod pm;
 
 use crate::ir::*;
+use crate::labels::*;
 use crate::pm::*;
 
 // FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index a0562083..d832e3a4 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,51 +1,9 @@
 use crate::ir::*;
+use crate::labels::*;
 use hercules_ir::{FunctionID, NodeID, ID};
 
 use std::collections::HashMap;
 
-#[derive(Debug, Clone)]
-pub enum LabelInfo {
-    Loop(NodeID),
-    Fork(NodeID),
-    Block(Vec<NodeID>),
-}
-
-#[derive(Debug, Clone)]
-pub struct ProgramLabels {
-    labels: Vec<Vec<LabelInfo>>,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct LabelID {
-    func: FunctionID,
-    label: usize,
-}
-
-impl ProgramLabels {
-    pub fn get_label(&self, label: LabelID) -> &LabelInfo {
-        &self.labels[label.func.idx()][label.label]
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct JunoFunctions {
-    functions: Vec<HashMap<FunctionID, HashMap<String, LabelID>>>,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct JunoFunctionID {
-    idx: usize,
-}
-
-impl JunoFunctions {
-    pub fn get_function(
-        &self,
-        func: JunoFunctionID,
-    ) -> &HashMap<FunctionID, HashMap<String, LabelID>> {
-        &self.functions[func.idx]
-    }
-}
-
 #[derive(Debug, Clone)]
 pub enum Value {
     Label { label: LabelID },
-- 
GitLab


From 4e1bff3118c2cfe5c55b380c908cb556684d9322 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 10:38:55 -0600
Subject: [PATCH 03/46] Sketch of schedule interpreter

---
 juno_scheduler/src/ir.rs |  9 +++++++++
 juno_scheduler/src/pm.rs | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 237e4f1a..c533c1f9 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -49,10 +49,19 @@ pub enum ScheduleExp {
     },
 }
 
+#[derive(Debug, Copy, Clone)]
+pub enum FixpointLimit {
+    NoLimit(),
+    PrintIter(),
+    StopAt(usize),
+    PanicAt(usize),
+}
+
 #[derive(Debug, Clone)]
 pub enum ScheduleStmt {
     Fixpoint {
         body: Box<ScheduleStmt>,
+        limit: FixpointLimit,
     },
     Block {
         body: Vec<ScheduleStmt>,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index d832e3a4..e49c83cf 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,6 +1,6 @@
 use crate::ir::*;
 use crate::labels::*;
-use hercules_ir::{FunctionID, NodeID, ID};
+use hercules_ir::*;
 
 use std::collections::HashMap;
 
@@ -10,3 +10,34 @@ pub enum Value {
     Function { func: JunoFunctionID },
     Record { fields: HashMap<String, Value> },
 }
+
+pub fn schedule(
+    module: Module,
+    schedule: ScheduleStmt,
+    mut env: HashMap<String, Value>,
+    mut labels: ProgramLabels,
+    mut functions: JunoFunctions,
+) -> Module {
+    schedule_interpret(module, &schedule, &mut env, &mut labels, &mut functions)
+}
+
+fn schedule_interpret(
+    mut module: Module,
+    schedule: &ScheduleStmt,
+    env: &mut HashMap<String, Value>,
+    labels: &mut ProgramLabels,
+    functions: &mut JunoFunctions,
+) -> Module {
+    match schedule {
+        ScheduleStmt::Fixpoint { .. } => todo!("fixpoint not implemented"),
+        ScheduleStmt::Block { body } => {
+            for command in body {
+                module = schedule_interpret(module, command, env, labels, functions);
+            }
+            module
+        }
+        ScheduleStmt::Assign { .. } => todo!(),
+        ScheduleStmt::AddSchedule { .. } => todo!(),
+        ScheduleStmt::AddDevice { .. } => todo!(),
+    }
+}
-- 
GitLab


From ada2cabcc204be2cfce849b0551adf7afec03837 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 11:01:17 -0600
Subject: [PATCH 04/46] Start implementing scheduler interpreter

---
 juno_scheduler/src/pm.rs | 95 +++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 11 deletions(-)

diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index e49c83cf..eb1aef92 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -6,38 +6,111 @@ use std::collections::HashMap;
 
 #[derive(Debug, Clone)]
 pub enum Value {
-    Label { label: LabelID },
+    Label { labels: Vec<LabelID> },
     Function { func: JunoFunctionID },
     Record { fields: HashMap<String, Value> },
 }
 
+#[derive(Debug, Clone)]
+pub enum SchedulerError {
+    UndefinedVariable(String),
+    UndefinedField(String),
+    UndefinedLabel(String),
+}
+
 pub fn schedule(
-    module: Module,
+    mut module: Module,
     schedule: ScheduleStmt,
     mut env: HashMap<String, Value>,
     mut labels: ProgramLabels,
     mut functions: JunoFunctions,
-) -> Module {
-    schedule_interpret(module, &schedule, &mut env, &mut labels, &mut functions)
+) -> Result<Module, SchedulerError> {
+    schedule_interpret(
+        &mut module,
+        &schedule,
+        &mut env,
+        &mut labels,
+        &mut functions,
+    )?;
+    Ok(module)
 }
 
 fn schedule_interpret(
-    mut module: Module,
+    module: &mut Module,
     schedule: &ScheduleStmt,
     env: &mut HashMap<String, Value>,
     labels: &mut ProgramLabels,
     functions: &mut JunoFunctions,
-) -> Module {
+) -> Result<(), SchedulerError> {
     match schedule {
         ScheduleStmt::Fixpoint { .. } => todo!("fixpoint not implemented"),
         ScheduleStmt::Block { body } => {
+            // FIXME: Scoping of the environment
+            for command in body {
+                schedule_interpret(module, command, env, labels, functions)?;
+            }
+        }
+        ScheduleStmt::Assign { var, exp } => {
+            let res = interp_expr(module, exp, env, labels, functions)?;
+            env.insert(var.clone(), res);
+        }
+        ScheduleStmt::AddSchedule { .. } => todo!("schedules not implemented"),
+        ScheduleStmt::AddDevice { .. } => todo!("devices not implemented"),
+    }
+    Ok(())
+}
+
+fn interp_expr(
+    module: &mut Module,
+    expr: &ScheduleExp,
+    env: &mut HashMap<String, Value>,
+    labels: &mut ProgramLabels,
+    functions: &mut JunoFunctions,
+) -> Result<Value, SchedulerError> {
+    match expr {
+        ScheduleExp::Variable { var } => match env.get(var) {
+            None => Err(SchedulerError::UndefinedVariable(var.clone())),
+            Some(v) => Ok(v.clone()),
+        },
+        ScheduleExp::Field { collect, field } => {
+            match interp_expr(module, collect, env, labels, functions)? {
+                Value::Label { .. } => Err(SchedulerError::UndefinedField(field.clone())),
+                Value::Function { func } => {
+                    let function_info = functions.get_function(func);
+                    let mut labels = vec![];
+                    for (_, func_labels) in function_info {
+                        match func_labels.get(field) {
+                            None => {
+                                return Err(SchedulerError::UndefinedLabel(field.clone()));
+                            }
+                            Some(label) => labels.push(*label),
+                        }
+                    }
+                    Ok(Value::Label { labels })
+                }
+                Value::Record { fields } => match fields.get(field) {
+                    None => Err(SchedulerError::UndefinedField(field.clone())),
+                    Some(v) => Ok(v.clone()),
+                },
+            }
+        }
+        ScheduleExp::RunPass { .. } => todo!("passes not implemented"),
+        ScheduleExp::Record { fields } => {
+            let mut result = HashMap::new();
+            for (field, val) in fields {
+                result.insert(
+                    field.clone(),
+                    interp_expr(module, val, env, labels, functions)?,
+                );
+            }
+            Ok(Value::Record { fields: result })
+        }
+        ScheduleExp::Block { body, res } => {
+            // FIXME: Scoping of the environment
             for command in body {
-                module = schedule_interpret(module, command, env, labels, functions);
+                schedule_interpret(module, command, env, labels, functions)?;
             }
-            module
+            interp_expr(module, res, env, labels, functions)
         }
-        ScheduleStmt::Assign { .. } => todo!(),
-        ScheduleStmt::AddSchedule { .. } => todo!(),
-        ScheduleStmt::AddDevice { .. } => todo!(),
     }
 }
-- 
GitLab


From c72014280af45252223b7a8e62d067a4ab710779 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 11:09:29 -0600
Subject: [PATCH 05/46] Create juno_utils for environment

---
 Cargo.lock                               |  6 ++++++
 Cargo.toml                               |  1 +
 juno_frontend/Cargo.toml                 |  1 +
 juno_frontend/src/lib.rs                 |  1 -
 juno_frontend/src/semant.rs              |  4 +++-
 juno_scheduler/Cargo.toml                |  1 +
 juno_scheduler/src/pm.rs                 |  3 +++
 juno_utils/.gitignore                    |  4 ++++
 juno_utils/Cargo.toml                    | 11 +++++++++++
 {juno_frontend => juno_utils}/src/env.rs |  0
 juno_utils/src/lib.rs                    |  1 +
 11 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 juno_utils/.gitignore
 create mode 100644 juno_utils/Cargo.toml
 rename {juno_frontend => juno_utils}/src/env.rs (100%)
 create mode 100644 juno_utils/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 73ab201c..c1d96edb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -724,6 +724,7 @@ dependencies = [
  "hercules_ir",
  "hercules_opt",
  "juno_scheduler",
+ "juno_utils",
  "lrlex",
  "lrpar",
  "num-rational",
@@ -757,6 +758,7 @@ version = "0.0.1"
 dependencies = [
  "cfgrammar",
  "hercules_ir",
+ "juno_utils",
  "lrlex",
  "lrpar",
 ]
@@ -770,6 +772,10 @@ dependencies = [
  "with_builtin_macros",
 ]
 
+[[package]]
+name = "juno_utils"
+version = "0.1.0"
+
 [[package]]
 name = "kv-log-macro"
 version = "1.0.7"
diff --git a/Cargo.toml b/Cargo.toml
index badc4260..cb8a8317 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ members = [
 	
 	"hercules_tools/hercules_driver",
 
+	"juno_utils",
 	"juno_frontend",
 	"juno_scheduler",
 	"juno_build",
diff --git a/juno_frontend/Cargo.toml b/juno_frontend/Cargo.toml
index 39e18baa..ad35b84c 100644
--- a/juno_frontend/Cargo.toml
+++ b/juno_frontend/Cargo.toml
@@ -29,3 +29,4 @@ phf = { version = "0.11", features = ["macros"] }
 hercules_ir = { path = "../hercules_ir" }
 hercules_opt = { path = "../hercules_opt" }
 juno_scheduler = { path = "../juno_scheduler" }
+juno_utils = { path = "../juno_utils" }
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index b18b2979..392dd74d 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -1,6 +1,5 @@
 mod codegen;
 mod dynconst;
-mod env;
 mod intrinsics;
 mod labeled_builder;
 mod locs;
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index 1ccdf3ed..a64551f7 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -1,4 +1,5 @@
 extern crate hercules_ir;
+extern crate juno_utils;
 
 use std::collections::{HashMap, LinkedList};
 use std::fmt;
@@ -12,7 +13,6 @@ use lrpar::NonStreamingLexer;
 use ordered_float::OrderedFloat;
 
 use crate::dynconst::DynConst;
-use crate::env::Env;
 use crate::intrinsics;
 use crate::locs::{span_to_loc, Location};
 use crate::parser;
@@ -20,6 +20,8 @@ use crate::parser::*;
 use crate::types;
 use crate::types::{Either, Type, TypeSolver};
 
+use self::juno_utils::env::Env;
+
 // Definitions and data structures for semantic analysis
 
 // Entities in the environment
diff --git a/juno_scheduler/Cargo.toml b/juno_scheduler/Cargo.toml
index 49e5f4a3..c41b9310 100644
--- a/juno_scheduler/Cargo.toml
+++ b/juno_scheduler/Cargo.toml
@@ -14,3 +14,4 @@ cfgrammar = "0.13"
 lrlex = "0.13"
 lrpar = "0.13"
 hercules_ir = { path = "../hercules_ir" }
+juno_utils = { path = "../juno_utils" }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index eb1aef92..05b173b2 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -2,6 +2,9 @@ use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
 
+extern crate juno_utils;
+use self::juno_utils::env::Env;
+
 use std::collections::HashMap;
 
 #[derive(Debug, Clone)]
diff --git a/juno_utils/.gitignore b/juno_utils/.gitignore
new file mode 100644
index 00000000..ef5f7e55
--- /dev/null
+++ b/juno_utils/.gitignore
@@ -0,0 +1,4 @@
+*.aux
+*.log
+*.out
+*.pdf
diff --git a/juno_utils/Cargo.toml b/juno_utils/Cargo.toml
new file mode 100644
index 00000000..0e153d1c
--- /dev/null
+++ b/juno_utils/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "juno_utils"
+version = "0.1.0"
+authors = ["Aaron Councilman <aaronjc4@illinois.edu>"]
+edition = "2021"
+
+[lib]
+name = "juno_utils"
+path = "src/lib.rs"
+
+[dependencies]
diff --git a/juno_frontend/src/env.rs b/juno_utils/src/env.rs
similarity index 100%
rename from juno_frontend/src/env.rs
rename to juno_utils/src/env.rs
diff --git a/juno_utils/src/lib.rs b/juno_utils/src/lib.rs
new file mode 100644
index 00000000..3d7924f6
--- /dev/null
+++ b/juno_utils/src/lib.rs
@@ -0,0 +1 @@
+pub mod env;
-- 
GitLab


From fe5eb2d07116fa6c75d6b284b18710c7993ee855 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 11:21:16 -0600
Subject: [PATCH 06/46] Scope variables in scheduler

---
 juno_scheduler/src/ir.rs |  8 ++++++--
 juno_scheduler/src/pm.rs | 32 +++++++++++++++++++++++---------
 juno_utils/src/env.rs    |  7 +++++++
 3 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index c533c1f9..7a6bbe57 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -30,7 +30,7 @@ pub enum Selector {
 #[derive(Debug, Clone)]
 pub enum ScheduleExp {
     Variable {
-        var: String,
+        var: usize,
     },
     Field {
         collect: Box<ScheduleExp>,
@@ -66,8 +66,12 @@ pub enum ScheduleStmt {
     Block {
         body: Vec<ScheduleStmt>,
     },
+    Let {
+        var: usize,
+        exp: ScheduleExp,
+    },
     Assign {
-        var: String,
+        var: usize,
         exp: ScheduleExp,
     },
     AddSchedule {
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 05b173b2..c0d13f53 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -16,7 +16,7 @@ pub enum Value {
 
 #[derive(Debug, Clone)]
 pub enum SchedulerError {
-    UndefinedVariable(String),
+    UndefinedVariable(usize),
     UndefinedField(String),
     UndefinedLabel(String),
 }
@@ -24,7 +24,7 @@ pub enum SchedulerError {
 pub fn schedule(
     mut module: Module,
     schedule: ScheduleStmt,
-    mut env: HashMap<String, Value>,
+    mut env: Env<usize, Value>,
     mut labels: ProgramLabels,
     mut functions: JunoFunctions,
 ) -> Result<Module, SchedulerError> {
@@ -41,22 +41,34 @@ pub fn schedule(
 fn schedule_interpret(
     module: &mut Module,
     schedule: &ScheduleStmt,
-    env: &mut HashMap<String, Value>,
+    env: &mut Env<usize, Value>,
     labels: &mut ProgramLabels,
     functions: &mut JunoFunctions,
 ) -> Result<(), SchedulerError> {
     match schedule {
         ScheduleStmt::Fixpoint { .. } => todo!("fixpoint not implemented"),
         ScheduleStmt::Block { body } => {
-            // FIXME: Scoping of the environment
+            env.open_scope();
             for command in body {
                 schedule_interpret(module, command, env, labels, functions)?;
             }
+            env.close_scope();
         }
-        ScheduleStmt::Assign { var, exp } => {
+        ScheduleStmt::Let { var, exp } => {
             let res = interp_expr(module, exp, env, labels, functions)?;
             env.insert(var.clone(), res);
         }
+        ScheduleStmt::Assign { var, exp } => {
+            let res = interp_expr(module, exp, env, labels, functions)?;
+            match env.lookup_mut(&var) {
+                None => {
+                    return Err(SchedulerError::UndefinedVariable(*var));
+                }
+                Some(val) => {
+                    *val = res;
+                }
+            }
+        }
         ScheduleStmt::AddSchedule { .. } => todo!("schedules not implemented"),
         ScheduleStmt::AddDevice { .. } => todo!("devices not implemented"),
     }
@@ -66,12 +78,12 @@ fn schedule_interpret(
 fn interp_expr(
     module: &mut Module,
     expr: &ScheduleExp,
-    env: &mut HashMap<String, Value>,
+    env: &mut Env<usize, Value>,
     labels: &mut ProgramLabels,
     functions: &mut JunoFunctions,
 ) -> Result<Value, SchedulerError> {
     match expr {
-        ScheduleExp::Variable { var } => match env.get(var) {
+        ScheduleExp::Variable { var } => match env.lookup(var) {
             None => Err(SchedulerError::UndefinedVariable(var.clone())),
             Some(v) => Ok(v.clone()),
         },
@@ -109,11 +121,13 @@ fn interp_expr(
             Ok(Value::Record { fields: result })
         }
         ScheduleExp::Block { body, res } => {
-            // FIXME: Scoping of the environment
+            env.open_scope();
             for command in body {
                 schedule_interpret(module, command, env, labels, functions)?;
             }
-            interp_expr(module, res, env, labels, functions)
+            let res = interp_expr(module, res, env, labels, functions);
+            env.close_scope();
+            res
         }
     }
 }
diff --git a/juno_utils/src/env.rs b/juno_utils/src/env.rs
index fb746045..cfa84b78 100644
--- a/juno_utils/src/env.rs
+++ b/juno_utils/src/env.rs
@@ -24,6 +24,13 @@ impl<K: Eq + Hash + Copy, V> Env<K, V> {
         }
     }
 
+    pub fn lookup_mut(&mut self, k: &K) -> Option<&mut V> {
+        match self.table.get_mut(k) {
+            None => None,
+            Some(l) => l.last_mut(),
+        }
+    }
+
     pub fn insert(&mut self, k: K, v: V) {
         if self.scope[self.scope.len() - 1].contains(&k) {
             match self.table.get_mut(&k) {
-- 
GitLab


From 26111ee9daa3ad1af8d05395171217205dc175b5 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 30 Dec 2024 11:53:02 -0600
Subject: [PATCH 07/46] Handling schedules and devices

---
 juno_scheduler/src/ir.rs     |  4 +-
 juno_scheduler/src/labels.rs | 17 ++++++--
 juno_scheduler/src/pm.rs     | 80 +++++++++++++++++++++++++++++++++++-
 3 files changed, 94 insertions(+), 7 deletions(-)

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 7a6bbe57..b09c08ae 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -1,6 +1,6 @@
 extern crate hercules_ir;
 
-use self::hercules_ir::ir::{FunctionSchedule, Schedule};
+use self::hercules_ir::ir::{Device, Schedule};
 
 #[derive(Debug, Clone)]
 pub enum Pass {
@@ -79,7 +79,7 @@ pub enum ScheduleStmt {
         on: Selector,
     },
     AddDevice {
-        device: FunctionSchedule,
+        device: Device,
         on: Selector,
     },
 }
diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
index 85552789..8f8885c2 100644
--- a/juno_scheduler/src/labels.rs
+++ b/juno_scheduler/src/labels.rs
@@ -1,4 +1,3 @@
-use crate::ir::*;
 use hercules_ir::{FunctionID, NodeID, ID};
 
 use std::collections::HashMap;
@@ -10,6 +9,18 @@ pub enum LabelInfo {
     Block(Vec<NodeID>),
 }
 
+impl IntoIterator for LabelInfo {
+    type Item = NodeID;
+    type IntoIter = std::vec::IntoIter<NodeID>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        match self {
+            LabelInfo::Loop(node) | LabelInfo::Fork(node) => vec![node].into_iter(),
+            LabelInfo::Block(nodes) => nodes.into_iter(),
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct ProgramLabels {
     labels: Vec<Vec<LabelInfo>>,
@@ -22,8 +33,8 @@ pub struct LabelID {
 }
 
 impl ProgramLabels {
-    pub fn get_label(&self, label: LabelID) -> &LabelInfo {
-        &self.labels[label.func.idx()][label.label]
+    pub fn get_label(&self, label: LabelID) -> (FunctionID, &LabelInfo) {
+        (label.func, &self.labels[label.func.idx()][label.label])
     }
 }
 
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index c0d13f53..e065e4bd 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -19,6 +19,7 @@ pub enum SchedulerError {
     UndefinedVariable(usize),
     UndefinedField(String),
     UndefinedLabel(String),
+    SemanticError(String),
 }
 
 pub fn schedule(
@@ -69,8 +70,58 @@ fn schedule_interpret(
                 }
             }
         }
-        ScheduleStmt::AddSchedule { .. } => todo!("schedules not implemented"),
-        ScheduleStmt::AddDevice { .. } => todo!("devices not implemented"),
+        ScheduleStmt::AddSchedule { sched, on } => match on {
+            Selector::Everything() => {
+                return Err(SchedulerError::SemanticError(
+                    "Cannot apply schedule to everything".to_string(),
+                ));
+            }
+            Selector::Selection(selection) => {
+                for label in selection {
+                    match interp_expr(module, label, env, labels, functions)? {
+                        Value::Label { labels: label_ids } => {
+                            add_schedule(module, labels, sched.clone(), &label_ids);
+                        }
+                        Value::Function { .. } => {
+                            return Err(SchedulerError::SemanticError(
+                                "Cannot apply schedule to function".to_string(),
+                            ));
+                        }
+                        Value::Record { .. } => {
+                            return Err(SchedulerError::SemanticError(
+                                "Cannot apply schedule to record".to_string(),
+                            ));
+                        }
+                    }
+                }
+            }
+        },
+        ScheduleStmt::AddDevice { device, on } => match on {
+            Selector::Everything() => {
+                return Err(SchedulerError::SemanticError(
+                    "Cannot apply device to everything".to_string(),
+                ));
+            }
+            Selector::Selection(selection) => {
+                for func in selection {
+                    match interp_expr(module, func, env, labels, functions)? {
+                        Value::Label { .. } => {
+                            return Err(SchedulerError::SemanticError(
+                                "Cannot apply device to label".to_string(),
+                            ));
+                        }
+                        Value::Function { func } => {
+                            add_device(module, functions, device.clone(), func);
+                        }
+                        Value::Record { .. } => {
+                            return Err(SchedulerError::SemanticError(
+                                "Cannot apply device to record".to_string(),
+                            ));
+                        }
+                    }
+                }
+            }
+        },
     }
     Ok(())
 }
@@ -131,3 +182,28 @@ fn interp_expr(
         }
     }
 }
+
+fn add_schedule(
+    module: &mut Module,
+    labels: &ProgramLabels,
+    sched: Schedule,
+    label_ids: &Vec<LabelID>,
+) {
+    for label in label_ids {
+        let (func, nodes) = labels.get_label(*label);
+        for node in nodes.clone().into_iter() {
+            module.functions[func.idx()].schedules[node.idx()].push(sched.clone());
+        }
+    }
+}
+
+fn add_device(
+    module: &mut Module,
+    functions: &JunoFunctions,
+    device: Device,
+    func: JunoFunctionID,
+) {
+    for (func, _) in functions.get_function(func) {
+        module.functions[func.idx()].device = Some(device.clone());
+    }
+}
-- 
GitLab


From 430f06d2c195c20c852230cb999ad88f96dec648 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 13:03:50 -0600
Subject: [PATCH 08/46] New schedule grammar

---
 juno_scheduler/src/compile.rs |  10 ++
 juno_scheduler/src/lang.l     |  32 +++++-
 juno_scheduler/src/lang.y     | 177 ++++++++++++++++++++++++----------
 juno_scheduler/src/lib.rs     |   3 +
 juno_scheduler/src/pm.rs      |   3 +-
 5 files changed, 168 insertions(+), 57 deletions(-)
 create mode 100644 juno_scheduler/src/compile.rs

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
new file mode 100644
index 00000000..1f955ea4
--- /dev/null
+++ b/juno_scheduler/src/compile.rs
@@ -0,0 +1,10 @@
+use crate::parser::*;
+use crate::ir::*;
+
+use juno_utils::env::Env;
+
+pub enum ScheduleError { }
+
+pub fn compile_schedule(sched: OperationList) -> Result<ScheduleStmt, ScheduleError> {
+    todo!()
+}
diff --git a/juno_scheduler/src/lang.l b/juno_scheduler/src/lang.l
index e6526c74..a8be4e94 100644
--- a/juno_scheduler/src/lang.l
+++ b/juno_scheduler/src/lang.l
@@ -13,15 +13,37 @@
 [\n\r]          ;
 
 ,               ","
+:               ":"
+;               ";"
+=               "="
+@               "@"
+\*              "*"
+\.              "."
 
+apply           "apply"
+fixpoint        "fixpoint"
+int             "int_keyword"
+let             "let"
+macro           "macro_keyword"
+on              "on"
+set             "set"
+target          "target"
+
+\(              "("
+\)              ")"
+\<              "<"
+\>              ">"
+\[              "["
+\]              "]"
 \{              "{"
 \}              "}"
 
-function        "function"
-on              "on"
-partition       "partition"
+panic[\t \n\r]+after "panic_after"
+print[\t \n\r]+iter  "print_iter"
+stop[\t \n\r]+after  "stop_after"
 
-[a-zA-Z][a-zA-Z0-9_]* "ID"
-@[a-zA-Z0-9_]+        "LABEL"
+[a-zA-Z][a-zA-Z0-9_]*! "MACRO"
+[a-zA-Z][a-zA-Z0-9_]*  "ID"
+[0-9]+                 "INT"
 
 .                     "UNMATCHED"
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index e7d98dba..74ec8182 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -1,61 +1,107 @@
 %start Schedule
 
-%avoid_insert "ID" "LABEL"
+%avoid_insert "ID" "INT"
 %expect-unused Unmatched 'UNMATCHED'
 
 %%
 
-Schedule -> Vec<FuncDirectives> : FunctionList { $1 };
+Schedule -> OperationList
+  :               { OperationList::NilStmt() }
+  | Expr          { OperationList::FinalExpr($1) }
+  | Stmt Schedule { OperationList::ConsStmt($1, Box::new($2)) }
+  ;
 
-FunctionList -> Vec<FuncDirectives>
-  : { vec![] }
-  | FunctionList FunctionDef { snoc($1, $2) }
+Stmt -> Stmt
+  : 'let' 'ID' '=' Expr ';'
+      { Stmt::LetStmt { span: $span, var: span_of_tok($2), expr: $4 } }
+  | Expr '=' Expr ';'
+      { Stmt::AssignStmt { span: $span, lhs: $1, rhs: $3 } }
+  | Expr ';'
+      { Stmt::ExprStmt { span: $span, exp: $1 } }
+  | 'fixpoint' FixpointLimit '{' Schedule '}'
+      { Stmt::Fixpoint { span: $span, limit: $2, body: Box::new($4) } }
+  | MacroDecl
+      { Stmt::MacroDecl { span: $span, def: $1 } }
   ;
 
-FunctionDef -> FuncDirectives
-  : 'function' Func '{' DirectiveList '}'
-    { FuncDirectives { span : $span, func : $2, directives : $4 }};
+FixpointLimit -> FixpointLimit
+  : { FixpointLimit::NoLimit { span: $span } }
+  | 'stop_after' 'INT'
+      { FixpointLimit::StopAfter { span: $span, limit: span_of_tok($2) } }
+  | 'panic_after' 'INT'
+      { FixpointLimit::PanicAfter { span: $span, limit: span_of_tok($2) } }
+  | 'print_iter'
+      { FixpointLimit::PrintIter { span: $span } }
+  ;
 
-DirectiveList -> Vec<Directive>
-  : { vec![] }
-  | DirectiveList Directive { snoc($1, $2) }
+Expr -> Expr
+  : 'ID' Args Selector
+      { Expr::Function { span: $span, name: span_of_tok($1), args: $2, selection: $3 } }
+  | 'MACRO' Args Selector
+      { Expr::Macro { span: $span, name: span_of_tok($1), args: $2, selection: $3 } }
+  | 'ID'
+      { Expr::Variable { span: span_of_tok($1) } }
+  | 'INT'
+      { Expr::Integer { span: span_of_tok($1) } }
+  | Expr '.' 'ID'
+      { Expr::Field { span: $span, lhs: Box::new($1), field: span_of_tok($3) } }
+  | Expr '@' 'ID'
+      { Expr::Field { span: $span, lhs: Box::new($1), field: span_of_tok($3) } }
+  | '(' Expr ')'
+      { $2 }
+  | '{' Schedule '}'
+      { Expr::BlockExpr { span: $span, body: Box::new($2) } }
+  | '<' Fields '>'
+      { Expr::Record { span: $span, fields: rev($2) } }
   ;
 
-Directive -> Directive
-  : 'partition' Labels 'on' Devices
-      { Directive::Partition { span : $span, labels : $2, devices : $4 } }
-  | 'ID' Labels
-      { Directive::Schedule { span : $span, command : span_of_tok($1), args : $2 } }
+Args -> Vec<Expr>
+  :               { vec![] }
+  | '[' Exprs ']' { rev($2) }
   ;
 
-Func -> Func
-  : 'ID' { Func { span : $span, name : $span, }}
+Exprs -> Vec<Expr>
+  :                 { vec![] }
+  | Expr            { vec![$1] }
+  | Expr ',' Exprs  { snoc($1, $3) }
   ;
 
-Labels -> Vec<Span>
-  : 'LABEL' { vec![span_of_tok($1)] }
-  | '{' LabelsRev '}' { rev($2) }
+Fields -> Vec<(Span, Expr)>
+  :                           { vec![] }
+  | 'ID' '=' Expr             { vec![(span_of_tok($1), $3)] }
+  | 'ID' '=' Expr ',' Fields  { snoc((span_of_tok($1), $3), $5) }
   ;
-LabelsRev -> Vec<Span>
-  : { vec![] }
-  | 'LABEL' { vec![span_of_tok($1)] }
-  | 'LABEL' ',' LabelsRev { cons(span_of_tok($1), $3) }
+
+Selector -> Selector
+  : '(' '*' ')'
+      { Selector::SelectAll { span: $span } }
+  | '(' Exprs ')'
+      { Selector::SelectExprs { span: $span, exprs: $2 } }
   ;
 
-Devices -> Vec<Device>
-  : Device { vec![$1] }
-  | '{' SomeDevices '}' { $2 }
+MacroDecl -> MacroDecl
+  : 'macro_keyword' 'MACRO' Params '(' 'ID' ')' MacroDef
+      { MacroDecl {
+          name: span_of_tok($2),
+          params: rev($3),
+          selection_name: span_of_tok($5),
+          def: Box::new($7),
+        }
+      }
   ;
-SomeDevices -> Vec<Device>
-  : Device { vec![$1] }
-  | SomeDevices ',' Device { snoc($1, $3) }
+
+Params -> Vec<(Span, Type)>
+  :                           { vec![] }
+  | 'ID' ':' Type             { vec![(span_of_tok($1), $3)] }
+  | 'ID' ':' Type ',' Params  { snoc((span_of_tok($1), $3), $5) }
   ;
 
-Device -> Device
-  : 'ID'
-      { Device { span : $span, name : span_of_tok($1), } }
+Type -> Type
+  : 'int_keyword' { Type::Int { span: $span  } }
   ;
 
+MacroDef -> OperationList : '{' Schedule '}' { $2 };
+
 Unmatched -> () : 'UNMATCHED' {};
 
 %%
@@ -63,32 +109,63 @@ Unmatched -> () : 'UNMATCHED' {};
 use cfgrammar::Span;
 use lrlex::DefaultLexeme;
 
+fn snoc<T>(x: T, mut xs: Vec<T>) -> Vec<T> {
+  xs.push(x);
+  xs
+}
+
+fn rev<T>(mut xs: Vec<T>) -> Vec<T> {
+  xs.reverse();
+  xs
+}
+
 fn span_of_tok(t : Result<DefaultLexeme, DefaultLexeme>) -> Span {
   t.map_err(|_| ()).map(|l| l.span()).unwrap()
 }
 
-fn cons<A>(hd : A, mut tl : Vec<A>) -> Vec<A> {
-  tl.push(hd);
-  tl
+pub enum OperationList {
+  NilStmt(),
+  FinalExpr(Expr),
+  ConsStmt(Stmt, Box<OperationList>),
+}
+
+pub enum Stmt {
+  LetStmt    { span: Span, var: Span, expr: Expr },
+  AssignStmt { span: Span, lhs: Expr, rhs: Expr },
+  ExprStmt   { span: Span, exp: Expr },
+  Fixpoint   { span: Span, limit: FixpointLimit, body: Box<OperationList> },
+  MacroDecl  { span: Span, def: MacroDecl },
 }
 
-fn snoc<A>(mut hd : Vec<A>, tl : A) -> Vec<A> {
-  hd.push(tl);
-  hd
+pub enum FixpointLimit {
+  NoLimit    { span: Span },
+  StopAfter  { span: Span, limit: Span },
+  PanicAfter { span: Span, limit: Span },
+  PrintIter  { span: Span },
 }
 
-fn rev<A>(mut lst : Vec<A>) -> Vec<A> {
-  lst.reverse();
-  lst
+pub enum Expr {
+  Function    { span: Span, name: Span, args: Vec<Expr>, selection: Selector },
+  Macro       { span: Span, name: Span, args: Vec<Expr>, selection: Selector },
+  Variable    { span: Span },
+  Integer     { span: Span },
+  Field       { span: Span, lhs: Box<Expr>, field: Span },
+  BlockExpr   { span: Span, body: Box<OperationList> },
+  Record      { span: Span, fields: Vec<(Span, Expr)> },
 }
 
-pub struct Func { pub span : Span, pub name : Span, }
-pub struct Device { pub span : Span, pub name : Span, }
+pub enum Selector {
+  SelectAll   { span: Span },
+  SelectExprs { span: Span, exprs: Vec<Expr> },
+}
 
-pub struct FuncDirectives { pub span : Span, pub func : Func,
-                            pub directives : Vec<Directive> }
+pub struct MacroDecl {
+  pub name: Span,
+  pub params: Vec<(Span, Type)>,
+  pub selection_name: Span,
+  pub def: Box<OperationList>,
+}
 
-pub enum Directive {
-  Schedule  { span : Span, command : Span, args : Vec<Span> },
-  Partition { span : Span, labels : Vec<Span>, devices : Vec<Device> },
+pub enum Type {
+  Int { span: Span },
 }
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index d0eafac3..21f811c8 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -1,4 +1,5 @@
 extern crate hercules_ir;
+extern crate juno_utils;
 
 use std::collections::{HashMap, HashSet};
 use std::fs::File;
@@ -15,10 +16,12 @@ use crate::parser::lexer;
 mod ir;
 mod labels;
 mod pm;
+mod compile;
 
 use crate::ir::*;
 use crate::labels::*;
 use crate::pm::*;
+use crate::compile::*;
 
 // FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
 // - The map from label names to their numbers
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index e065e4bd..8dce783e 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -2,8 +2,7 @@ use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
 
-extern crate juno_utils;
-use self::juno_utils::env::Env;
+use juno_utils::env::Env;
 
 use std::collections::HashMap;
 
-- 
GitLab


From f53e6705f873075fb6501136cf531a3f14f96f0c Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 13:35:17 -0600
Subject: [PATCH 09/46] Rename juno_utils to hercules_utils, add string table

---
 Cargo.lock                                | 12 +++----
 Cargo.toml                                |  2 +-
 {juno_utils => hercules_utils}/.gitignore |  0
 {juno_utils => hercules_utils}/Cargo.toml |  4 +--
 {juno_utils => hercules_utils}/src/env.rs |  0
 hercules_utils/src/lib.rs                 |  2 ++
 hercules_utils/src/stringtab.rs           | 38 +++++++++++++++++++++
 juno_frontend/Cargo.toml                  |  2 +-
 juno_frontend/src/semant.rs               | 40 ++---------------------
 juno_scheduler/Cargo.toml                 |  2 +-
 juno_scheduler/src/compile.rs             |  2 +-
 juno_scheduler/src/lib.rs                 |  2 +-
 juno_scheduler/src/pm.rs                  |  2 +-
 juno_utils/src/lib.rs                     |  1 -
 14 files changed, 57 insertions(+), 52 deletions(-)
 rename {juno_utils => hercules_utils}/.gitignore (100%)
 rename {juno_utils => hercules_utils}/Cargo.toml (74%)
 rename {juno_utils => hercules_utils}/src/env.rs (100%)
 create mode 100644 hercules_utils/src/lib.rs
 create mode 100644 hercules_utils/src/stringtab.rs
 delete mode 100644 juno_utils/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 9a7a6e9b..b7b5c76e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -651,6 +651,10 @@ dependencies = [
  "take_mut",
 ]
 
+[[package]]
+name = "hercules_utils"
+version = "0.1.0"
+
 [[package]]
 name = "hermit-abi"
 version = "0.4.0"
@@ -732,8 +736,8 @@ dependencies = [
  "clap",
  "hercules_ir",
  "hercules_opt",
+ "hercules_utils",
  "juno_scheduler",
- "juno_utils",
  "lrlex",
  "lrpar",
  "num-rational",
@@ -776,7 +780,7 @@ version = "0.0.1"
 dependencies = [
  "cfgrammar",
  "hercules_ir",
- "juno_utils",
+ "hercules_utils",
  "lrlex",
  "lrpar",
 ]
@@ -790,10 +794,6 @@ dependencies = [
  "with_builtin_macros",
 ]
 
-[[package]]
-name = "juno_utils"
-version = "0.1.0"
-
 [[package]]
 name = "kv-log-macro"
 version = "1.0.7"
diff --git a/Cargo.toml b/Cargo.toml
index f147261a..0f814021 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,13 @@
 [workspace]
 resolver = "2"
 members = [
+	"hercules_utils",
 	"hercules_cg",
 	"hercules_ir",
 	"hercules_opt",
 	
 	"hercules_tools/hercules_driver",
 
-	"juno_utils",
 	"juno_frontend",
 	"juno_scheduler",
 	"juno_build",
diff --git a/juno_utils/.gitignore b/hercules_utils/.gitignore
similarity index 100%
rename from juno_utils/.gitignore
rename to hercules_utils/.gitignore
diff --git a/juno_utils/Cargo.toml b/hercules_utils/Cargo.toml
similarity index 74%
rename from juno_utils/Cargo.toml
rename to hercules_utils/Cargo.toml
index 0e153d1c..d085c02a 100644
--- a/juno_utils/Cargo.toml
+++ b/hercules_utils/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
-name = "juno_utils"
+name = "hercules_utils"
 version = "0.1.0"
 authors = ["Aaron Councilman <aaronjc4@illinois.edu>"]
 edition = "2021"
 
 [lib]
-name = "juno_utils"
+name = "hercules_utils"
 path = "src/lib.rs"
 
 [dependencies]
diff --git a/juno_utils/src/env.rs b/hercules_utils/src/env.rs
similarity index 100%
rename from juno_utils/src/env.rs
rename to hercules_utils/src/env.rs
diff --git a/hercules_utils/src/lib.rs b/hercules_utils/src/lib.rs
new file mode 100644
index 00000000..56b404be
--- /dev/null
+++ b/hercules_utils/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod env;
+pub mod stringtab;
diff --git a/hercules_utils/src/stringtab.rs b/hercules_utils/src/stringtab.rs
new file mode 100644
index 00000000..19b6d3a9
--- /dev/null
+++ b/hercules_utils/src/stringtab.rs
@@ -0,0 +1,38 @@
+use std::collections::HashMap;
+
+// Map strings to unique identifiers and counts uids
+#[derive(Clone, Debug)]
+pub struct StringTable {
+    count: usize,
+    string_to_index: HashMap<String, usize>,
+    index_to_string: HashMap<usize, String>,
+}
+
+impl StringTable {
+    pub fn new() -> StringTable {
+        StringTable {
+            count: 0,
+            string_to_index: HashMap::new(),
+            index_to_string: HashMap::new(),
+        }
+    }
+
+    // Produce the UID for a string
+    pub fn lookup_string(&mut self, s: String) -> usize {
+        match self.string_to_index.get(&s) {
+            Some(n) => *n,
+            None => {
+                let n = self.count;
+                self.count += 1;
+                self.string_to_index.insert(s.clone(), n);
+                self.index_to_string.insert(n, s);
+                n
+            }
+        }
+    }
+
+    // Identify the string corresponding to a UID
+    pub fn lookup_id(&self, n: usize) -> Option<String> {
+        self.index_to_string.get(&n).cloned()
+    }
+}
diff --git a/juno_frontend/Cargo.toml b/juno_frontend/Cargo.toml
index ad35b84c..21aba0e4 100644
--- a/juno_frontend/Cargo.toml
+++ b/juno_frontend/Cargo.toml
@@ -29,4 +29,4 @@ phf = { version = "0.11", features = ["macros"] }
 hercules_ir = { path = "../hercules_ir" }
 hercules_opt = { path = "../hercules_opt" }
 juno_scheduler = { path = "../juno_scheduler" }
-juno_utils = { path = "../juno_utils" }
+hercules_utils = { path = "../hercules_utils" }
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index a64551f7..048b64dc 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -1,5 +1,5 @@
 extern crate hercules_ir;
-extern crate juno_utils;
+extern crate hercules_utils;
 
 use std::collections::{HashMap, LinkedList};
 use std::fmt;
@@ -20,7 +20,8 @@ use crate::parser::*;
 use crate::types;
 use crate::types::{Either, Type, TypeSolver};
 
-use self::juno_utils::env::Env;
+use self::hercules_utils::env::Env;
+use self::hercules_utils::stringtab::StringTable;
 
 // Definitions and data structures for semantic analysis
 
@@ -80,41 +81,6 @@ impl PartialEq for Literal {
 
 impl Eq for Literal {}
 
-// Map strings to unique identifiers and counts uids
-struct StringTable {
-    count: usize,
-    string_to_index: HashMap<String, usize>,
-    index_to_string: HashMap<usize, String>,
-}
-impl StringTable {
-    fn new() -> StringTable {
-        StringTable {
-            count: 0,
-            string_to_index: HashMap::new(),
-            index_to_string: HashMap::new(),
-        }
-    }
-
-    // Produce the UID for a string
-    fn lookup_string(&mut self, s: String) -> usize {
-        match self.string_to_index.get(&s) {
-            Some(n) => *n,
-            None => {
-                let n = self.count;
-                self.count += 1;
-                self.string_to_index.insert(s.clone(), n);
-                self.index_to_string.insert(n, s);
-                n
-            }
-        }
-    }
-
-    // Identify the string corresponding to a UID
-    fn lookup_id(&self, n: usize) -> Option<String> {
-        self.index_to_string.get(&n).cloned()
-    }
-}
-
 // Maps label names to unique identifiers (numbered 0..n for each function)
 // Also tracks the map from function names to their numbers
 struct LabelSet {
diff --git a/juno_scheduler/Cargo.toml b/juno_scheduler/Cargo.toml
index c41b9310..f0802422 100644
--- a/juno_scheduler/Cargo.toml
+++ b/juno_scheduler/Cargo.toml
@@ -14,4 +14,4 @@ cfgrammar = "0.13"
 lrlex = "0.13"
 lrpar = "0.13"
 hercules_ir = { path = "../hercules_ir" }
-juno_utils = { path = "../juno_utils" }
+hercules_utils = { path = "../hercules_utils" }
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 1f955ea4..746d089d 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -1,7 +1,7 @@
 use crate::parser::*;
 use crate::ir::*;
 
-use juno_utils::env::Env;
+use hercules_utils::env::Env;
 
 pub enum ScheduleError { }
 
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 21f811c8..d3e5d323 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -1,5 +1,5 @@
 extern crate hercules_ir;
-extern crate juno_utils;
+extern crate hercules_utils;
 
 use std::collections::{HashMap, HashSet};
 use std::fs::File;
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 8dce783e..f0b63aa4 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -2,7 +2,7 @@ use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
 
-use juno_utils::env::Env;
+use hercules_utils::env::Env;
 
 use std::collections::HashMap;
 
diff --git a/juno_utils/src/lib.rs b/juno_utils/src/lib.rs
deleted file mode 100644
index 3d7924f6..00000000
--- a/juno_utils/src/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod env;
-- 
GitLab


From b2639259b604ec793cc78f8e348a233d6b214c41 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 13:55:57 -0600
Subject: [PATCH 10/46] Add labels to Hercules IR

---
 Cargo.lock                      |  5 +++
 hercules_ir/Cargo.toml          |  3 +-
 hercules_ir/src/build.rs        | 12 ++++++-
 hercules_ir/src/ir.rs           | 12 +++++++
 hercules_ir/src/parse.rs        |  8 ++++-
 hercules_ir/src/typecheck.rs    |  1 +
 hercules_opt/Cargo.toml         |  1 +
 hercules_opt/src/editor.rs      | 58 +++++++++++++++++++++++++++++++--
 hercules_opt/src/outline.rs     | 10 +++++-
 hercules_opt/src/pass.rs        | 44 ++++++++++++++++++++++++-
 hercules_utils/Cargo.toml       |  1 +
 hercules_utils/src/stringtab.rs | 10 +++++-
 12 files changed, 157 insertions(+), 8 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b7b5c76e..058dd3f9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -630,6 +630,7 @@ name = "hercules_ir"
 version = "0.1.0"
 dependencies = [
  "bitvec",
+ "hercules_utils",
  "nom",
  "ordered-float",
  "rand",
@@ -644,6 +645,7 @@ dependencies = [
  "either",
  "hercules_cg",
  "hercules_ir",
+ "hercules_utils",
  "itertools",
  "ordered-float",
  "postcard",
@@ -654,6 +656,9 @@ dependencies = [
 [[package]]
 name = "hercules_utils"
 version = "0.1.0"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "hermit-abi"
diff --git a/hercules_ir/Cargo.toml b/hercules_ir/Cargo.toml
index 44648c11..43875fac 100644
--- a/hercules_ir/Cargo.toml
+++ b/hercules_ir/Cargo.toml
@@ -8,4 +8,5 @@ rand = "*"
 nom = "*"
 ordered-float = { version = "*", features = ["serde"] }
 bitvec = "*"
-serde = { version = "*", features = ["derive"] }
\ No newline at end of file
+serde = { version = "*", features = ["derive"] }
+hercules_utils = { path = "../hercules_utils" }
diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index 78c4eca4..5968b07c 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 
 use crate::*;
 
@@ -37,6 +37,7 @@ pub struct NodeBuilder {
     function_id: FunctionID,
     node: Node,
     schedules: Vec<Schedule>,
+    labels: Vec<usize>,
 }
 
 /*
@@ -473,6 +474,7 @@ impl<'a> Builder<'a> {
             entry,
             nodes: vec![Node::Start],
             schedules: vec![vec![]],
+            labels: vec![HashSet::new()],
             device: None,
         });
         Ok((id, NodeID::new(0)))
@@ -484,11 +486,13 @@ impl<'a> Builder<'a> {
             .nodes
             .push(Node::Start);
         self.module.functions[function.idx()].schedules.push(vec![]);
+        self.module.functions[function.idx()].labels.push(HashSet::new());
         NodeBuilder {
             id,
             function_id: function,
             node: Node::Start,
             schedules: vec![],
+            labels: vec![],
         }
     }
 
@@ -499,6 +503,8 @@ impl<'a> Builder<'a> {
         self.module.functions[builder.function_id.idx()].nodes[builder.id.idx()] = builder.node;
         self.module.functions[builder.function_id.idx()].schedules[builder.id.idx()] =
             builder.schedules;
+        self.module.functions[builder.function_id.idx()].labels[builder.id.idx()] =
+            builder.labels.into_iter().collect();
         Ok(())
     }
 }
@@ -617,4 +623,8 @@ impl NodeBuilder {
     pub fn add_schedule(&mut self, schedule: Schedule) {
         self.schedules.push(schedule);
     }
+
+    pub fn add_label(&mut self, label: usize) {
+        self.labels.push(label);
+    }
 }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 8bb8e4ef..97609de8 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -1,7 +1,9 @@
 extern crate bitvec;
 extern crate ordered_float;
 extern crate serde;
+extern crate hercules_utils;
 
+use std::collections::HashSet;
 use std::fmt::Write;
 use std::ops::Coroutine;
 use std::ops::CoroutineState;
@@ -11,6 +13,8 @@ use self::bitvec::prelude::*;
 use self::serde::Deserialize;
 use self::serde::Serialize;
 
+use self::hercules_utils::stringtab::StringTable;
+
 use crate::*;
 
 /*
@@ -25,6 +29,7 @@ pub struct Module {
     pub types: Vec<Type>,
     pub constants: Vec<Constant>,
     pub dynamic_constants: Vec<DynamicConstant>,
+    pub labels: StringTable,
 }
 
 /*
@@ -46,6 +51,7 @@ pub struct Function {
     pub nodes: Vec<Node>,
 
     pub schedules: FunctionSchedule,
+    pub labels: FunctionLabel,
     pub device: Option<Device>,
 }
 
@@ -343,6 +349,11 @@ pub enum Device {
  */
 pub type FunctionSchedule = Vec<Vec<Schedule>>;
 
+/*
+ * A single node may have multiple labels.
+ */
+pub type FunctionLabel = Vec<HashSet<usize>>;
+
 impl Module {
     /*
      * Printing out types, constants, and dynamic constants fully requires a
@@ -730,6 +741,7 @@ impl Function {
 
         // Step 4: update the schedules.
         self.schedules.fix_gravestones(&node_mapping);
+        self.labels.fix_gravestones(&node_mapping);
 
         node_mapping
     }
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 5e4b1217..d4f2dc91 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -1,11 +1,14 @@
 extern crate nom;
+extern crate hercules_utils;
 
 use std::cell::RefCell;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::str::FromStr;
 
 use crate::*;
 
+use self::hercules_utils::stringtab::StringTable;
+
 /*
  * TODO: This parsing code was written before the generic build API was created.
  * As a result, this parsing code duplicates much of the interning logic the
@@ -126,6 +129,7 @@ fn parse_module<'a>(ir_text: &'a str, context: Context<'a>) -> nom::IResult<&'a
             entry: true,
             nodes: vec![],
             schedules: vec![],
+            labels: vec![],
             device: None,
         };
         context.function_ids.len()
@@ -159,6 +163,7 @@ fn parse_module<'a>(ir_text: &'a str, context: Context<'a>) -> nom::IResult<&'a
         types,
         constants,
         dynamic_constants,
+        labels: StringTable::new(),
     };
     Ok((rest, module))
 }
@@ -264,6 +269,7 @@ fn parse_function<'a>(
             entry: true,
             nodes: fixed_nodes,
             schedules: vec![vec![]; num_nodes],
+            labels: vec![HashSet::new(); num_nodes],
             device: None,
         },
     ))
diff --git a/hercules_ir/src/typecheck.rs b/hercules_ir/src/typecheck.rs
index d6862c35..b7e1a7a6 100644
--- a/hercules_ir/src/typecheck.rs
+++ b/hercules_ir/src/typecheck.rs
@@ -93,6 +93,7 @@ pub fn typecheck(
         ref mut types,
         ref constants,
         ref mut dynamic_constants,
+        ..
     } = module;
     let mut reverse_type_map: HashMap<Type, TypeID> = types
         .iter()
diff --git a/hercules_opt/Cargo.toml b/hercules_opt/Cargo.toml
index e1936a97..7adc8e15 100644
--- a/hercules_opt/Cargo.toml
+++ b/hercules_opt/Cargo.toml
@@ -13,3 +13,4 @@ postcard = { version = "*", features = ["alloc"] }
 serde = { version = "*", features = ["derive"] }
 hercules_cg = { path = "../hercules_cg" }
 hercules_ir = { path = "../hercules_ir" }
+hercules_utils = { path = "../hercules_utils" }
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 4ff08e69..4e521eee 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -1,6 +1,7 @@
 extern crate bitvec;
 extern crate either;
 extern crate hercules_ir;
+extern crate hercules_utils;
 extern crate itertools;
 
 use std::cell::{Ref, RefCell};
@@ -13,6 +14,7 @@ use self::either::Either;
 
 use self::hercules_ir::def_use::*;
 use self::hercules_ir::ir::*;
+use self::hercules_utils::stringtab::StringTable;
 
 /*
  * Helper object for editing Hercules functions in a trackable manner. Edits
@@ -31,6 +33,9 @@ pub struct FunctionEditor<'a> {
     constants: &'a RefCell<Vec<Constant>>,
     dynamic_constants: &'a RefCell<Vec<DynamicConstant>>,
     types: &'a RefCell<Vec<Type>>,
+    // Keep a RefCell to the string table that tracks labels, so that new labels
+    // can be added as needed
+    labels: &'a RefCell<StringTable>,
     // Most optimizations need def use info, so provide an iteratively updated
     // mutable version that's automatically updated based on recorded edits.
     mut_def_use: Vec<HashSet<NodeID>>,
@@ -56,6 +61,8 @@ pub struct FunctionEdit<'a: 'b, 'b> {
     added_and_updated_nodes: BTreeMap<NodeID, Node>,
     // Keep track of added and updated schedules.
     added_and_updated_schedules: BTreeMap<NodeID, Vec<Schedule>>,
+    // Keep track of added and updated labels.
+    added_and_updated_labels: BTreeMap<NodeID, HashSet<usize>>,
     // Keep track of added (dynamic) constants and types
     added_constants: Vec<Constant>,
     added_dynamic_constants: Vec<DynamicConstant>,
@@ -74,6 +81,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
         constants: &'a RefCell<Vec<Constant>>,
         dynamic_constants: &'a RefCell<Vec<DynamicConstant>>,
         types: &'a RefCell<Vec<Type>>,
+        labels: &'a RefCell<StringTable>,
         def_use: &ImmutableDefUseMap,
     ) -> Self {
         let mut_def_use = (0..function.nodes.len())
@@ -93,6 +101,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             constants,
             dynamic_constants,
             types,
+            labels,
             mut_def_use,
             mutable_nodes,
         }
@@ -109,6 +118,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             added_nodeids: HashSet::new(),
             added_and_updated_nodes: BTreeMap::new(),
             added_and_updated_schedules: BTreeMap::new(),
+            added_and_updated_labels: BTreeMap::new(),
             added_constants: Vec::new().into(),
             added_dynamic_constants: Vec::new().into(),
             added_types: Vec::new().into(),
@@ -126,6 +136,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 added_nodeids,
                 added_and_updated_nodes,
                 added_and_updated_schedules,
+                added_and_updated_labels,
                 added_constants,
                 added_dynamic_constants,
                 added_types,
@@ -162,7 +173,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 }
             }
 
-            // Step 3: add and update schedules.
+            // Step 3.0: add and update schedules.
             editor
                 .function
                 .schedules
@@ -171,6 +182,15 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 editor.function.schedules[id.idx()] = schedule;
             }
 
+            // Step 3.1: add and update labels.
+            editor
+                .function
+                .labels
+                .resize(editor.function.nodes.len(), HashSet::new());
+            for (id, label) in added_and_updated_labels {
+                editor.function.labels[id.idx()] = label;
+            }
+
             // Step 4: delete nodes. This is done using "gravestones", where a
             // node other than node ID 0 being a start node is considered a
             // gravestone.
@@ -180,7 +200,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 editor.function.nodes[id.idx()] = Node::Start;
             }
 
-            // Step 5: propagate schedules along sub-edit edges.
+            // Step 5.0: propagate schedules along sub-edit edges.
             for (src, dst) in sub_edits {
                 let mut dst_schedules = take(&mut editor.function.schedules[dst.idx()]);
                 for src_schedule in editor.function.schedules[src.idx()].iter() {
@@ -191,6 +211,8 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 editor.function.schedules[dst.idx()] = dst_schedules;
             }
 
+            // TODO Step 5.1: propagate labels
+
             // Step 6: update the length of mutable_nodes. All added nodes are
             // mutable.
             editor
@@ -443,6 +465,36 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
+    pub fn get_label(&self, id: NodeID) -> &HashSet<usize> {
+        // The user may get the labels of a to-be deleted node.
+        if let Some(label) = self.added_and_updated_labels.get(&id) {
+            // Refer to added or updated label.
+            label
+        } else {
+            // Refer to the origin label of this code.
+            &self.editor.function.labels[id.idx()]
+        }
+    }
+
+    pub fn add_label(mut self, id: NodeID, label: usize) -> Result<Self, Self> {
+        if self.is_mutable(id) {
+            if let Some(labels) = self.added_and_updated_labels.get_mut(&id) {
+                labels.insert(label);
+            } else {
+                let mut labels = self.editor.function.labels[id.idx()].clone();
+                labels.insert(label);
+                self.added_and_updated_labels.insert(id, labels);
+            }
+            Ok(self)
+        } else {
+            Err(self)
+        }
+    }
+
+    pub fn new_label(&mut self, name: String) -> usize {
+        self.editor.labels.borrow_mut().lookup_string(name)
+    }
+
     pub fn get_users(&self, id: NodeID) -> impl Iterator<Item = NodeID> + '_ {
         assert!(!self.deleted_nodeids.contains(&id));
         if let Some(users) = self.updated_def_use.get(&id) {
@@ -664,6 +716,7 @@ fn func(x: i32) -> i32
         let constants_ref = RefCell::new(src_module.constants);
         let dynamic_constants_ref = RefCell::new(src_module.dynamic_constants);
         let types_ref = RefCell::new(src_module.types);
+        let labels_ref = RefCell::new(src_module.labels);
         // Edit the function by replacing the add with a multiply.
         let mut editor = FunctionEditor::new(
             func,
@@ -671,6 +724,7 @@ fn func(x: i32) -> i32
             &constants_ref,
             &dynamic_constants_ref,
             &types_ref,
+            &labels_ref,
             &def_use(func),
         );
         let success = editor.edit(|mut edit| {
diff --git a/hercules_opt/src/outline.rs b/hercules_opt/src/outline.rs
index 84cedb76..ba656a2c 100644
--- a/hercules_opt/src/outline.rs
+++ b/hercules_opt/src/outline.rs
@@ -1,6 +1,6 @@
 extern crate hercules_ir;
 
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::{BTreeMap, BTreeSet, HashSet};
 use std::iter::zip;
 use std::sync::atomic::{AtomicUsize, Ordering};
 
@@ -205,6 +205,7 @@ pub fn outline(
             entry: false,
             nodes: vec![],
             schedules: vec![],
+            labels: vec![],
             device: None,
         };
 
@@ -422,6 +423,13 @@ pub fn outline(
             outlined.schedules[callee_id.idx()] = edit.get_schedule(*id).clone();
         }
 
+        // Copy the labels into the new callee.
+        outlined.labels.resize(outlined.nodes.len(), HashSet::new());
+        for id in partition.iter() {
+            let callee_id = convert_id(*id);
+            outlined.labels[callee_id.idx()] = edit.get_label(*id).clone();
+        }
+
         // Step 3: edit the original function to call the outlined function.
         let dynamic_constants = (0..edit.get_num_dynamic_constant_params())
             .map(|idx| edit.add_dynamic_constant(DynamicConstant::Parameter(idx as usize)))
diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs
index baeaae87..967441bc 100644
--- a/hercules_opt/src/pass.rs
+++ b/hercules_opt/src/pass.rs
@@ -291,12 +291,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         dce(&mut editor);
@@ -304,6 +306,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -317,6 +320,7 @@ impl PassManager {
                     let dynamic_constants_ref =
                         RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                     let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
 
                     let def_uses = self.def_uses.as_ref().unwrap();
 
@@ -332,6 +336,7 @@ impl PassManager {
                                 &constants_ref,
                                 &dynamic_constants_ref,
                                 &types_ref,
+                                &labels_ref,
                                 &def_uses[i],
                             )
                         })
@@ -342,6 +347,7 @@ impl PassManager {
                     self.module.constants = constants_ref.take();
                     self.module.dynamic_constants = dynamic_constants_ref.take();
                     self.module.types = types_ref.take();
+                    self.module.labels = labels_ref.take();
 
                     for func in self.module.functions.iter_mut() {
                         func.delete_gravestones();
@@ -360,12 +366,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         ccp(&mut editor, &reverse_postorders[idx]);
@@ -373,6 +381,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -387,12 +396,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         gvn(&mut editor, false);
@@ -400,6 +411,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -435,12 +447,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         phi_elim(&mut editor);
@@ -448,6 +462,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -511,12 +526,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         sroa(&mut editor, &reverse_postorders[idx], &typing[idx]);
@@ -524,6 +541,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -538,6 +556,7 @@ impl PassManager {
                     let dynamic_constants_ref =
                         RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                     let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                     let mut editors: Vec<_> = zip(
                         self.module.functions.iter_mut().enumerate(),
                         def_uses.iter(),
@@ -549,6 +568,7 @@ impl PassManager {
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             def_use,
                         )
                     })
@@ -558,6 +578,7 @@ impl PassManager {
                     self.module.constants = constants_ref.take();
                     self.module.dynamic_constants = dynamic_constants_ref.take();
                     self.module.types = types_ref.take();
+                    self.module.labels = labels_ref.take();
 
                     for func in self.module.functions.iter_mut() {
                         func.delete_gravestones();
@@ -571,6 +592,7 @@ impl PassManager {
                     let dynamic_constants_ref =
                         RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                     let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                     let old_num_funcs = self.module.functions.len();
                     let mut editors: Vec<_> = zip(
                         self.module.functions.iter_mut().enumerate(),
@@ -583,6 +605,7 @@ impl PassManager {
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             def_use,
                         )
                     })
@@ -594,6 +617,7 @@ impl PassManager {
                     self.module.constants = constants_ref.take();
                     self.module.dynamic_constants = dynamic_constants_ref.take();
                     self.module.types = types_ref.take();
+                    self.module.labels = labels_ref.take();
                     self.clear_analyses();
 
                     self.make_def_uses();
@@ -608,6 +632,7 @@ impl PassManager {
                     let dynamic_constants_ref =
                         RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                     let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                     let mut editors: Vec<_> = zip(
                         self.module.functions.iter_mut().enumerate(),
                         def_uses.iter(),
@@ -619,6 +644,7 @@ impl PassManager {
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             def_use,
                         )
                     })
@@ -640,6 +666,7 @@ impl PassManager {
                     self.module.constants = constants_ref.take();
                     self.module.dynamic_constants = dynamic_constants_ref.take();
                     self.module.types = types_ref.take();
+                    self.module.labels = labels_ref.take();
 
                     for func in self.module.functions.iter_mut() {
                         func.delete_gravestones();
@@ -656,6 +683,7 @@ impl PassManager {
                     let dynamic_constants_ref =
                         RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                     let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
 
                     // By default in an editor all nodes are mutable, which is desired in this case
                     // since we are only modifying the IDs of functions that we call.
@@ -670,6 +698,7 @@ impl PassManager {
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             def_use,
                         )
                     })
@@ -679,6 +708,7 @@ impl PassManager {
                     self.module.constants = constants_ref.take();
                     self.module.dynamic_constants = dynamic_constants_ref.take();
                     self.module.types = types_ref.take();
+                    self.module.labels = labels_ref.take();
 
                     for func in self.module.functions.iter_mut() {
                         func.delete_gravestones();
@@ -702,12 +732,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         fork_split(&mut editor, &fork_join_maps[idx], &reduce_cycles[idx]);
@@ -715,6 +747,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -731,12 +764,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         unforkify(&mut editor, &fork_join_maps[idx]);
@@ -744,6 +779,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -773,12 +809,14 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
+                            &labels_ref,
                             &def_uses[idx],
                         );
                         if let Some(bb) = legalize_reference_semantics(
@@ -798,6 +836,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
@@ -820,13 +859,15 @@ impl PassManager {
                         let dynamic_constants_ref =
                             RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
                         let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
+                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
                         let mut editor = FunctionEditor::new(
                             &mut self.module.functions[idx],
                             FunctionID::new(idx),
                             &constants_ref,
                             &dynamic_constants_ref,
                             &types_ref,
-                            &def_uses[idx],
+                            &labels_ref,
+                            &def_uses[idx]
                         );
                         infer_parallel_reduce(
                             &mut editor,
@@ -840,6 +881,7 @@ impl PassManager {
                         self.module.constants = constants_ref.take();
                         self.module.dynamic_constants = dynamic_constants_ref.take();
                         self.module.types = types_ref.take();
+                        self.module.labels = labels_ref.take();
 
                         self.module.functions[idx].delete_gravestones();
                     }
diff --git a/hercules_utils/Cargo.toml b/hercules_utils/Cargo.toml
index d085c02a..9b87ba5c 100644
--- a/hercules_utils/Cargo.toml
+++ b/hercules_utils/Cargo.toml
@@ -9,3 +9,4 @@ name = "hercules_utils"
 path = "src/lib.rs"
 
 [dependencies]
+serde = { version = "*", features = ["derive"] }
diff --git a/hercules_utils/src/stringtab.rs b/hercules_utils/src/stringtab.rs
index 19b6d3a9..ae9a9db1 100644
--- a/hercules_utils/src/stringtab.rs
+++ b/hercules_utils/src/stringtab.rs
@@ -1,7 +1,11 @@
+extern crate serde;
+
+use self::serde::{Deserialize, Serialize};
+
 use std::collections::HashMap;
 
 // Map strings to unique identifiers and counts uids
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct StringTable {
     count: usize,
     string_to_index: HashMap<String, usize>,
@@ -36,3 +40,7 @@ impl StringTable {
         self.index_to_string.get(&n).cloned()
     }
 }
+
+impl Default for StringTable {
+    fn default() -> Self { StringTable::new() }
+}
-- 
GitLab


From f822f0ea8bc8cd8bcf8efef22923cf680b7cfc78 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 15:10:29 -0600
Subject: [PATCH 11/46] Generate labeled IR from Juno

---
 hercules_ir/src/build.rs             |  24 +++++-
 hercules_ir/src/ir.rs                |   5 +-
 hercules_ir/src/parse.rs             |   2 +-
 hercules_opt/src/editor.rs           |  33 +++++++--
 juno_build/src/lib.rs                |   2 +-
 juno_frontend/src/codegen.rs         | 105 +++++++++------------------
 juno_frontend/src/labeled_builder.rs |  91 ++++++-----------------
 juno_frontend/src/lib.rs             |   4 +-
 juno_frontend/src/semant.rs          |  64 +++-------------
 juno_scheduler/src/labels.rs         |  57 +--------------
 juno_scheduler/src/pm.rs             |   6 +-
 11 files changed, 126 insertions(+), 267 deletions(-)

diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index 5968b07c..facb04e6 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -11,10 +11,11 @@ pub struct Builder<'a> {
     // Intern function names.
     function_ids: HashMap<&'a str, FunctionID>,
 
-    // Intern types, constants, and dynamic constants on a per-module basis.
+    // Intern types, constants, dynamic constants, and labels on a per-module basis.
     interned_types: HashMap<Type, TypeID>,
     interned_constants: HashMap<Constant, ConstantID>,
     interned_dynamic_constants: HashMap<DynamicConstant, DynamicConstantID>,
+    interned_labels: HashMap<String, LabelID>,
 
     // For product, summation, and array constant creation, it's useful to know
     // the type of each constant.
@@ -80,6 +81,17 @@ impl<'a> Builder<'a> {
         }
     }
 
+    pub fn add_label(&mut self, label: &String) -> LabelID {
+        if let Some(id) = self.interned_labels.get(label) {
+            *id
+        } else {
+            let id = self.interned_labels.len();
+            self.interned_labels.insert(label.clone(), id);
+            self.module.labels.push(label.clone());
+            id
+        }
+    }
+
     pub fn create() -> Self {
         Self::default()
     }
@@ -453,6 +465,10 @@ impl<'a> Builder<'a> {
         Index::Position(idx)
     }
 
+    pub fn get_labels(&self, func: FunctionID, node: NodeID) -> &HashSet<LabelID> {
+        &self.module.functions[func.idx()].labels[node.idx()]
+    }
+
     pub fn create_function(
         &mut self,
         name: &str,
@@ -624,7 +640,11 @@ impl NodeBuilder {
         self.schedules.push(schedule);
     }
 
-    pub fn add_label(&mut self, label: usize) {
+    pub fn add_label(&mut self, label: LabelID) {
         self.labels.push(label);
     }
+
+    pub fn add_labels(&mut self, label: Vec<LabelID>) {
+        self.labels.extend(label);
+    }
 }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 97609de8..9a72204c 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -29,7 +29,7 @@ pub struct Module {
     pub types: Vec<Type>,
     pub constants: Vec<Constant>,
     pub dynamic_constants: Vec<DynamicConstant>,
-    pub labels: StringTable,
+    pub labels: Vec<String>,
 }
 
 /*
@@ -352,7 +352,8 @@ pub type FunctionSchedule = Vec<Vec<Schedule>>;
 /*
  * A single node may have multiple labels.
  */
-pub type FunctionLabel = Vec<HashSet<usize>>;
+pub type LabelID = usize;
+pub type FunctionLabel = Vec<HashSet<LabelID>>;
 
 impl Module {
     /*
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index d4f2dc91..88bb8105 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -163,7 +163,7 @@ fn parse_module<'a>(ir_text: &'a str, context: Context<'a>) -> nom::IResult<&'a
         types,
         constants,
         dynamic_constants,
-        labels: StringTable::new(),
+        labels: vec![],
     };
     Ok((rest, module))
 }
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 4e521eee..e79354b7 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -35,7 +35,7 @@ pub struct FunctionEditor<'a> {
     types: &'a RefCell<Vec<Type>>,
     // Keep a RefCell to the string table that tracks labels, so that new labels
     // can be added as needed
-    labels: &'a RefCell<StringTable>,
+    labels: &'a RefCell<Vec<String>>,
     // Most optimizations need def use info, so provide an iteratively updated
     // mutable version that's automatically updated based on recorded edits.
     mut_def_use: Vec<HashSet<NodeID>>,
@@ -63,10 +63,11 @@ pub struct FunctionEdit<'a: 'b, 'b> {
     added_and_updated_schedules: BTreeMap<NodeID, Vec<Schedule>>,
     // Keep track of added and updated labels.
     added_and_updated_labels: BTreeMap<NodeID, HashSet<usize>>,
-    // Keep track of added (dynamic) constants and types
+    // Keep track of added (dynamic) constants, types, and labels
     added_constants: Vec<Constant>,
     added_dynamic_constants: Vec<DynamicConstant>,
     added_types: Vec<Type>,
+    added_labels: Vec<String>,
     // Compute a def-use map entries iteratively.
     updated_def_use: BTreeMap<NodeID, HashSet<NodeID>>,
     updated_return_type: Option<TypeID>,
@@ -81,7 +82,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
         constants: &'a RefCell<Vec<Constant>>,
         dynamic_constants: &'a RefCell<Vec<DynamicConstant>>,
         types: &'a RefCell<Vec<Type>>,
-        labels: &'a RefCell<StringTable>,
+        labels: &'a RefCell<Vec<String>>,
         def_use: &ImmutableDefUseMap,
     ) -> Self {
         let mut_def_use = (0..function.nodes.len())
@@ -122,6 +123,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             added_constants: Vec::new().into(),
             added_dynamic_constants: Vec::new().into(),
             added_types: Vec::new().into(),
+            added_labels: Vec::new().into(),
             updated_def_use: BTreeMap::new(),
             updated_return_type: None,
             sub_edits: vec![],
@@ -140,6 +142,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 added_constants,
                 added_dynamic_constants,
                 added_types,
+                added_labels,
                 updated_def_use,
                 updated_return_type,
                 sub_edits,
@@ -211,7 +214,8 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 editor.function.schedules[dst.idx()] = dst_schedules;
             }
 
-            // TODO Step 5.1: propagate labels
+            // Step 5.1: update and (TODO) propagate labels
+            editor.labels.borrow_mut().extend(added_labels);
 
             // Step 6: update the length of mutable_nodes. All added nodes are
             // mutable.
@@ -465,7 +469,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
-    pub fn get_label(&self, id: NodeID) -> &HashSet<usize> {
+    pub fn get_label(&self, id: NodeID) -> &HashSet<LabelID> {
         // The user may get the labels of a to-be deleted node.
         if let Some(label) = self.added_and_updated_labels.get(&id) {
             // Refer to added or updated label.
@@ -476,7 +480,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
-    pub fn add_label(mut self, id: NodeID, label: usize) -> Result<Self, Self> {
+    pub fn add_label(mut self, id: NodeID, label: LabelID) -> Result<Self, Self> {
         if self.is_mutable(id) {
             if let Some(labels) = self.added_and_updated_labels.get_mut(&id) {
                 labels.insert(label);
@@ -491,8 +495,21 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
-    pub fn new_label(&mut self, name: String) -> usize {
-        self.editor.labels.borrow_mut().lookup_string(name)
+    pub fn new_label(&mut self, name: String) -> LabelID {
+        let pos = self
+            .editor
+            .labels
+            .borrow()
+            .iter()
+            .chain(self.added_labels.iter())
+            .position(|l| *l == name);
+        if let Some(idx) = pos {
+            idx
+        } else {
+            let id = self.editor.labels.borrow().len() + self.added_labels.len();
+            self.added_labels.push(name);
+            id
+        }
     }
 
     pub fn get_users(&self, id: NodeID) -> impl Iterator<Item = NodeID> + '_ {
diff --git a/juno_build/src/lib.rs b/juno_build/src/lib.rs
index fdaf4d27..726e9c3b 100644
--- a/juno_build/src/lib.rs
+++ b/juno_build/src/lib.rs
@@ -218,7 +218,7 @@ impl JunoCompiler {
                 return Err("Unable to parse Hercules IR file.".to_string());
             };
 
-            match compile_ir(ir_mod, None, verify, x_dot, schedule, out_dir, module_name) {
+            match compile_ir(ir_mod, verify, x_dot, schedule, out_dir, module_name) {
                 Ok(()) => Ok(()),
                 Err(errs) => Err(format!("{}", errs)),
             }
diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 31c85ae9..2053bb33 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -2,7 +2,6 @@ use std::collections::{HashMap, HashSet, VecDeque};
 
 use hercules_ir::ir;
 use hercules_ir::ir::*;
-use juno_scheduler::{FunctionMap, LabeledStructure};
 
 use crate::labeled_builder::LabeledBuilder;
 use crate::semant;
@@ -13,29 +12,7 @@ use crate::types::{Either, Primitive, TypeSolver, TypeSolverInst};
 // Loop info is a stack of the loop levels, recording the latch and exit block of each
 type LoopInfo = Vec<(NodeID, NodeID)>;
 
-fn merge_function_maps(
-    mut functions: HashMap<(usize, Vec<TypeID>), FunctionID>,
-    funcs: &Vec<Function>,
-    mut tree: HashMap<FunctionID, Vec<(LabeledStructure, HashSet<usize>)>>,
-    mut labels: HashMap<FunctionID, HashMap<NodeID, usize>>,
-) -> FunctionMap {
-    let mut res = HashMap::new();
-    for ((func_num, type_vars), func_id) in functions.drain() {
-        let func_labels = tree.remove(&func_id).unwrap();
-        let node_labels = labels.remove(&func_id).unwrap();
-        let func_info = res.entry(func_num).or_insert((
-            funcs[func_num].label_map.clone(),
-            funcs[func_num].name.clone(),
-            vec![],
-        ));
-        func_info
-            .2
-            .push((type_vars, func_id, func_labels, node_labels));
-    }
-    res
-}
-
-pub fn codegen_program(prg: Prg) -> (Module, FunctionMap) {
+pub fn codegen_program(prg: Prg) -> Module {
     CodeGenerator::build(prg)
 }
 
@@ -54,7 +31,7 @@ struct CodeGenerator<'a> {
 }
 
 impl CodeGenerator<'_> {
-    fn build((types, funcs): Prg) -> (Module, FunctionMap) {
+    fn build(Prg { types, funcs, labels }: Prg) -> Module {
         // Identify the functions (by index) which have no type arguments, these are the ones we
         // ask for code to be generated for
         let func_idx =
@@ -64,7 +41,7 @@ impl CodeGenerator<'_> {
                 .filter_map(|(i, f)| if f.num_type_args == 0 { Some(i) } else { None });
 
         let mut codegen = CodeGenerator {
-            builder: LabeledBuilder::create(),
+            builder: LabeledBuilder::create(labels),
             types: &types,
             funcs: &funcs,
             uid: 0,
@@ -80,7 +57,7 @@ impl CodeGenerator<'_> {
         codegen.finish()
     }
 
-    fn finish(mut self) -> (Module, FunctionMap) {
+    fn finish(mut self) -> Module {
         while !self.worklist.is_empty() {
             let (idx, mut type_inst, func, entry) = self.worklist.pop_front().unwrap();
             self.builder.set_function(func);
@@ -95,11 +72,8 @@ impl CodeGenerator<'_> {
             functions,
             worklist: _,
         } = self;
-        let (module, label_tree, label_map) = builder.finish();
-        (
-            module,
-            merge_function_maps(functions, funcs, label_tree, label_map),
-        )
+
+        builder.finish()
     }
 
     fn get_function(&mut self, func_idx: usize, ty_args: Vec<TypeID>) -> FunctionID {
@@ -138,7 +112,6 @@ impl CodeGenerator<'_> {
                         param_types,
                         return_type,
                         func.num_dyn_consts as u32,
-                        func.num_labels,
                         func.entry,
                     )
                     .unwrap();
@@ -164,7 +137,7 @@ impl CodeGenerator<'_> {
         }
 
         // Generate code for the body
-        let (_, None) = self.codegen_stmt(&func.body, types, &mut ssa, entry, &mut vec![]) else {
+        let None = self.codegen_stmt(&func.body, types, &mut ssa, entry, &mut vec![]) else {
             panic!("Generated code for a function missing a return")
         };
     }
@@ -176,42 +149,39 @@ impl CodeGenerator<'_> {
         ssa: &mut SSA,
         cur_block: NodeID,
         loops: &mut LoopInfo,
-    ) -> (LabeledStructure, Option<NodeID>) {
+    ) -> Option<NodeID> {
         match stmt {
             Stmt::AssignStmt { var, val } => {
                 let (val, block) = self.codegen_expr(val, types, ssa, cur_block);
                 ssa.write_variable(*var, block, val);
-                (LabeledStructure::Expression(val), Some(block))
+                Some(block)
             }
             Stmt::IfStmt { cond, thn, els } => {
                 let (val_cond, block_cond) = self.codegen_expr(cond, types, ssa, cur_block);
                 let (mut if_node, block_then, block_else) =
                     ssa.create_cond(&mut self.builder, block_cond);
 
-                let (_, then_end) = self.codegen_stmt(thn, types, ssa, block_then, loops);
+                let then_end = self.codegen_stmt(thn, types, ssa, block_then, loops);
                 let else_end = match els {
                     None => Some(block_else),
-                    Some(els_stmt) => self.codegen_stmt(els_stmt, types, ssa, block_else, loops).1,
+                    Some(els_stmt) => self.codegen_stmt(els_stmt, types, ssa, block_else, loops),
                 };
 
                 let if_id = if_node.id();
                 if_node.build_if(block_cond, val_cond);
                 self.builder.add_node(if_node);
 
-                (
-                    LabeledStructure::Branch(if_id),
-                    match (then_end, else_end) {
-                        (None, els) => els,
-                        (thn, None) => thn,
-                        (Some(then_term), Some(else_term)) => {
-                            let block_join = ssa.create_block(&mut self.builder);
-                            ssa.add_pred(block_join, then_term);
-                            ssa.add_pred(block_join, else_term);
-                            ssa.seal_block(block_join, &mut self.builder);
-                            Some(block_join)
-                        }
-                    },
-                )
+                match (then_end, else_end) {
+                    (None, els) => els,
+                    (thn, None) => thn,
+                    (Some(then_term), Some(else_term)) => {
+                        let block_join = ssa.create_block(&mut self.builder);
+                        ssa.add_pred(block_join, then_term);
+                        ssa.add_pred(block_join, else_term);
+                        ssa.seal_block(block_join, &mut self.builder);
+                        Some(block_join)
+                    }
+                }
             }
             Stmt::LoopStmt { cond, update, body } => {
                 // We generate guarded loops, so the first step is to create
@@ -236,7 +206,6 @@ impl CodeGenerator<'_> {
                     None => block_latch,
                     Some(stmt) => self
                         .codegen_stmt(stmt, types, ssa, block_latch, loops)
-                        .1
                         .expect("Loop update should return control"),
                 };
                 let (val_cond, block_cond) = self.codegen_expr(cond, types, ssa, block_updated);
@@ -258,7 +227,7 @@ impl CodeGenerator<'_> {
 
                 // Generate code for the body
                 loops.push((block_latch, block_exit));
-                let (_, body_res) = self.codegen_stmt(body, types, ssa, body_block, loops);
+                let body_res = self.codegen_stmt(body, types, ssa, body_block, loops);
                 loops.pop();
 
                 // If the body of the loop can reach some block, we add that block as a predecessor
@@ -276,51 +245,47 @@ impl CodeGenerator<'_> {
 
                 // It is always assumed a loop may be skipped and so control can reach after the
                 // loop
-                (LabeledStructure::Loop(body_block), Some(block_exit))
+                Some(block_exit)
             }
             Stmt::ReturnStmt { expr } => {
                 let (val_ret, block_ret) = self.codegen_expr(expr, types, ssa, cur_block);
                 let mut return_node = self.builder.allocate_node();
                 return_node.build_return(block_ret, val_ret);
                 self.builder.add_node(return_node);
-                (LabeledStructure::Expression(val_ret), None)
+                None
             }
             Stmt::BreakStmt {} => {
                 let last_loop = loops.len() - 1;
                 let (_latch, exit) = loops[last_loop];
                 ssa.add_pred(exit, cur_block); // The block that contains this break now leads to
                                                // the exit
-                (LabeledStructure::Nothing(), None)
+                None
             }
             Stmt::ContinueStmt {} => {
                 let last_loop = loops.len() - 1;
                 let (latch, _exit) = loops[last_loop];
                 ssa.add_pred(latch, cur_block); // The block that contains this continue now leads
                                                 // to the latch
-                (LabeledStructure::Nothing(), None)
+                None
             }
-            Stmt::BlockStmt { body, label_last } => {
-                let mut label = None;
+            Stmt::BlockStmt { body } => {
                 let mut block = Some(cur_block);
                 for stmt in body.iter() {
-                    let (new_label, new_block) =
+                    let new_block =
                         self.codegen_stmt(stmt, types, ssa, block.unwrap(), loops);
                     block = new_block;
-                    if label.is_none() || *label_last {
-                        label = Some(new_label);
-                    }
                 }
-                (label.unwrap_or(LabeledStructure::Nothing()), block)
+                block
             }
             Stmt::ExprStmt { expr } => {
-                let (val, block) = self.codegen_expr(expr, types, ssa, cur_block);
-                (LabeledStructure::Expression(val), Some(block))
+                let (_val, block) = self.codegen_expr(expr, types, ssa, cur_block);
+                Some(block)
             }
             Stmt::LabeledStmt { label, stmt } => {
                 self.builder.push_label(*label);
-                let (labeled, res) = self.codegen_stmt(&*stmt, types, ssa, cur_block, loops);
-                self.builder.pop_label(labeled);
-                (labeled, res)
+                let res = self.codegen_stmt(&*stmt, types, ssa, cur_block, loops);
+                self.builder.pop_label();
+                res
             }
         }
     }
diff --git a/juno_frontend/src/labeled_builder.rs b/juno_frontend/src/labeled_builder.rs
index 3cbf36da..4d31332a 100644
--- a/juno_frontend/src/labeled_builder.rs
+++ b/juno_frontend/src/labeled_builder.rs
@@ -3,6 +3,8 @@ use hercules_ir::ir::*;
 use juno_scheduler::LabeledStructure;
 use std::collections::{HashMap, HashSet};
 
+use hercules_utils::stringtab::StringTable;
+
 // A label-tracking code generator which tracks the current function that we're
 // generating code for and the what labels apply to the nodes being created
 // FIXME: The builder public is currently public to avoid code duplication and
@@ -12,39 +14,21 @@ use std::collections::{HashMap, HashSet};
 pub struct LabeledBuilder<'a> {
     pub builder: Builder<'a>,
     function: Option<FunctionID>,
-    label: usize,
-    label_stack: Vec<usize>,
-    label_tree: HashMap<FunctionID, Vec<(LabeledStructure, HashSet<usize>)>>,
-    label_map: HashMap<FunctionID, HashMap<NodeID, usize>>,
+    label_stack: Vec<LabelID>,
+    label_tab: StringTable,
 }
 impl<'a> LabeledBuilder<'a> {
-    pub fn create() -> LabeledBuilder<'a> {
+    pub fn create(labels: StringTable) -> LabeledBuilder<'a> {
         LabeledBuilder {
             builder: Builder::create(),
             function: None,
-            label: 0, // 0 is always the root label
             label_stack: vec![],
-            label_tree: HashMap::new(),
-            label_map: HashMap::new(),
+            label_tab: labels,
         }
     }
 
-    pub fn finish(
-        self,
-    ) -> (
-        Module,
-        HashMap<FunctionID, Vec<(LabeledStructure, HashSet<usize>)>>,
-        HashMap<FunctionID, HashMap<NodeID, usize>>,
-    ) {
-        let LabeledBuilder {
-            builder,
-            function: _,
-            label: _,
-            label_stack: _,
-            label_tree,
-            label_map,
-        } = self;
-        (builder.finish(), label_tree, label_map)
+    pub fn finish(self) -> Module {
+        self.builder.finish()
     }
 
     pub fn create_function(
@@ -53,7 +37,6 @@ impl<'a> LabeledBuilder<'a> {
         param_types: Vec<TypeID>,
         return_type: TypeID,
         num_dynamic_constants: u32,
-        num_labels: usize,
         entry: bool,
     ) -> Result<(FunctionID, NodeID), String> {
         let (func, entry) = self.builder.create_function(
@@ -64,78 +47,48 @@ impl<'a> LabeledBuilder<'a> {
             entry,
         )?;
 
-        self.label_tree.insert(
-            func,
-            vec![(LabeledStructure::Nothing(), HashSet::new()); num_labels],
-        );
-        self.label_map.insert(func, HashMap::new());
-
         Ok((func, entry))
     }
 
     pub fn set_function(&mut self, func: FunctionID) {
         self.function = Some(func);
-        self.label = 0;
+        assert!(self.label_stack.is_empty());
     }
 
     pub fn push_label(&mut self, label: usize) {
-        let Some(cur_func) = self.function else {
-            panic!("Setting label without function")
+        let Some(label_str) = self.label_tab.lookup_id(label) else {
+            panic!("Label missing from string table")
         };
-
-        let cur_label = self.label;
-        self.label_stack.push(cur_label);
-
-        for ancestor in self.label_stack.iter() {
-            self.label_tree.get_mut(&cur_func).unwrap()[*ancestor]
-                .1
-                .insert(label);
-        }
-
-        self.label = label;
+        let label_id = self.builder.add_label(&label_str);
+        self.label_stack.push(label_id);
     }
 
-    pub fn pop_label(&mut self, structure: LabeledStructure) {
-        let Some(cur_func) = self.function else {
-            panic!("Setting label without function")
-        };
-        self.label_tree.get_mut(&cur_func).unwrap()[self.label].0 = structure;
-        let Some(label) = self.label_stack.pop() else {
-            panic!("Cannot pop label not pushed first")
+    pub fn pop_label(&mut self) {
+        let Some(_) = self.label_stack.pop() else {
+            panic!("No label to pop")
         };
-        self.label = label;
     }
 
-    fn allocate_node_labeled(&mut self, label: usize) -> NodeBuilder {
+    fn allocate_node_labeled(&mut self, label: Vec<LabelID>) -> NodeBuilder {
         let Some(func) = self.function else {
             panic!("Cannot allocate node without function")
         };
-        let builder = self.builder.allocate_node(func);
-
-        self.label_map
-            .get_mut(&func)
-            .unwrap()
-            .insert(builder.id(), label);
 
+        let mut builder = self.builder.allocate_node(func);
+        builder.add_labels(label);
         builder
     }
 
     pub fn allocate_node(&mut self) -> NodeBuilder {
-        self.allocate_node_labeled(self.label)
+        self.allocate_node_labeled(self.label_stack.clone())
     }
 
     pub fn allocate_node_labeled_with(&mut self, other: NodeID) -> NodeBuilder {
         let Some(func) = self.function else {
             panic!("Cannot allocate node without function")
         };
-        let label = self
-            .label_map
-            .get(&func)
-            .unwrap()
-            .get(&other)
-            .expect("Other node not labeled");
-
-        self.allocate_node_labeled(*label)
+        let labels = self.builder.get_labels(func, other).iter().cloned().collect::<Vec<_>>();
+        self.allocate_node_labeled(labels)
     }
 
     pub fn add_node(&mut self, builder: NodeBuilder) {
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index eba11de8..461f251d 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -95,11 +95,10 @@ pub fn compile(
             return Err(ErrorMessage::SemanticError(msg));
         }
     };
-    let (module, func_info) = codegen::codegen_program(prg);
+    let module = codegen::codegen_program(prg);
 
     compile_ir(
         module,
-        Some(func_info),
         verify,
         x_dot,
         schedule,
@@ -110,7 +109,6 @@ pub fn compile(
 
 pub fn compile_ir(
     module: hercules_ir::ir::Module,
-    func_info: Option<FunctionMap>,
     verify: JunoVerify,
     x_dot: bool,
     schedule: JunoSchedule,
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index 048b64dc..5f6ef092 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -81,37 +81,6 @@ impl PartialEq for Literal {
 
 impl Eq for Literal {}
 
-// Maps label names to unique identifiers (numbered 0..n for each function)
-// Also tracks the map from function names to their numbers
-struct LabelSet {
-    count: usize,
-    string_to_index: HashMap<String, usize>,
-}
-impl LabelSet {
-    fn new() -> LabelSet {
-        // Label number 0 is reserved to be the "root" label in code generation
-        LabelSet {
-            count: 1,
-            string_to_index: HashMap::from([("<root>".to_string(), 0)]),
-        }
-    }
-
-    // Inserts a string if it is not already contained in this set, if it is
-    // contained does nothing and returns the label back wrapped in an error,
-    // otherwise inserts and returns the new label's id wrapped in Ok
-    fn insert_new(&mut self, label: String) -> Result<usize, String> {
-        match self.string_to_index.get(&label) {
-            Some(_) => Err(label),
-            None => {
-                let uid = self.count;
-                self.count += 1;
-                self.string_to_index.insert(label, uid);
-                Ok(uid)
-            }
-        }
-    }
-}
-
 // Convert spans into uids in the String Table
 fn intern_id(
     n: &Span,
@@ -222,7 +191,11 @@ fn append_errors3<A, B, C>(
 
 // Normalized AST forms after semantic analysis
 // These include type information at all expression nodes, and remove names and locations
-pub type Prg = (TypeSolver, Vec<Function>);
+pub struct Prg {
+    pub types: TypeSolver,
+    pub funcs: Vec<Function>,
+    pub labels: StringTable,
+}
 
 // The function stores information for code-generation. The type information therefore is not the
 // type information that is needed for type checking code that uses this function.
@@ -233,8 +206,6 @@ pub struct Function {
     pub num_type_args: usize,
     pub arguments: Vec<(usize, Type)>,
     pub return_type: Type,
-    pub num_labels: usize,
-    pub label_map: HashMap<String, usize>,
     pub body: Stmt,
     pub entry: bool,
 }
@@ -274,7 +245,6 @@ pub enum Stmt {
     ContinueStmt {},
     BlockStmt {
         body: Vec<Stmt>,
-        label_last: bool,
     },
     ExprStmt {
         expr: Expr,
@@ -553,6 +523,7 @@ fn analyze_program(
     lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
 ) -> Result<Prg, ErrorMessages> {
     let mut stringtab = StringTable::new();
+    let mut labels = StringTable::new();
     let mut env: Env<usize, Entity> = Env::new();
     let mut types = TypeSolver::new();
 
@@ -859,9 +830,6 @@ fn analyze_program(
                     }
                 }
 
-                // Create a set of the labels in this function
-                let mut labels = LabelSet::new();
-
                 // Finally, we have a properly built environment and we can
                 // start processing the body
                 let (mut body, end_reachable) = process_stmt(
@@ -897,7 +865,6 @@ fn analyze_program(
                                     &mut types,
                                 ),
                             ],
-                            label_last: false,
                         };
                     } else {
                         Err(singleton_error(ErrorMessage::SemanticError(
@@ -935,8 +902,6 @@ fn analyze_program(
                         .map(|(v, n)| (*n, v.1))
                         .collect::<Vec<_>>(),
                     return_type: pure_return_type,
-                    num_labels: labels.count,
-                    label_map: labels.string_to_index,
                     body: body,
                     entry: entry,
                 });
@@ -968,7 +933,7 @@ fn analyze_program(
         }
     }
 
-    Ok((types, res))
+    Ok(Prg { types, funcs: res, labels })
 }
 
 fn process_type_def(
@@ -1656,7 +1621,7 @@ fn process_stmt(
     return_type: Type,
     inout_vars: &Vec<usize>,
     inout_types: &Vec<Type>,
-    labels: &mut LabelSet,
+    labels: &mut StringTable,
 ) -> Result<(Stmt, bool), ErrorMessages> {
     match stmt {
         parser::Stmt::LetStmt {
@@ -2256,9 +2221,6 @@ fn process_stmt(
                             body: Box::new(body),
                         },
                     ],
-                    // A label applied to this loop should be applied to the
-                    // loop, not the initialization
-                    label_last: true,
                 },
                 true,
             ))
@@ -2411,7 +2373,6 @@ fn process_stmt(
                 Ok((
                     Stmt::BlockStmt {
                         body: res,
-                        label_last: false,
                     },
                     reachable,
                 ))
@@ -2451,14 +2412,7 @@ fn process_stmt(
             stmt,
         } => {
             let label_str = lexer.span_str(label).to_string();
-
-            let label_id = match labels.insert_new(label_str) {
-                Err(label_str) => Err(singleton_error(ErrorMessage::SemanticError(
-                    span_to_loc(label, lexer),
-                    format!("Label {} already exists", label_str),
-                )))?,
-                Ok(id) => id,
-            };
+            let label_id = labels.lookup_string(label_str);
 
             let (body, reach_end) = process_stmt(
                 *stmt,
diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
index 8f8885c2..ba9a7506 100644
--- a/juno_scheduler/src/labels.rs
+++ b/juno_scheduler/src/labels.rs
@@ -1,58 +1,7 @@
-use hercules_ir::{FunctionID, NodeID, ID};
-
-use std::collections::HashMap;
-
-#[derive(Debug, Clone)]
-pub enum LabelInfo {
-    Loop(NodeID),
-    Fork(NodeID),
-    Block(Vec<NodeID>),
-}
-
-impl IntoIterator for LabelInfo {
-    type Item = NodeID;
-    type IntoIter = std::vec::IntoIter<NodeID>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        match self {
-            LabelInfo::Loop(node) | LabelInfo::Fork(node) => vec![node].into_iter(),
-            LabelInfo::Block(nodes) => nodes.into_iter(),
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct ProgramLabels {
-    labels: Vec<Vec<LabelInfo>>,
-}
+use hercules_ir::ir::*;
 
 #[derive(Debug, Copy, Clone)]
-pub struct LabelID {
+pub struct LabelInfo {
     func: FunctionID,
-    label: usize,
-}
-
-impl ProgramLabels {
-    pub fn get_label(&self, label: LabelID) -> (FunctionID, &LabelInfo) {
-        (label.func, &self.labels[label.func.idx()][label.label])
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct JunoFunctions {
-    functions: Vec<HashMap<FunctionID, HashMap<String, LabelID>>>,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct JunoFunctionID {
-    idx: usize,
-}
-
-impl JunoFunctions {
-    pub fn get_function(
-        &self,
-        func: JunoFunctionID,
-    ) -> &HashMap<FunctionID, HashMap<String, LabelID>> {
-        &self.functions[func.idx]
-    }
+    label: LabelID,
 }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index f0b63aa4..e2cca9e8 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,3 +1,4 @@
+/*
 use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
@@ -8,7 +9,7 @@ use std::collections::HashMap;
 
 #[derive(Debug, Clone)]
 pub enum Value {
-    Label { labels: Vec<LabelID> },
+    Label { labels: Vec<LabelInfo> },
     Function { func: JunoFunctionID },
     Record { fields: HashMap<String, Value> },
 }
@@ -186,7 +187,7 @@ fn add_schedule(
     module: &mut Module,
     labels: &ProgramLabels,
     sched: Schedule,
-    label_ids: &Vec<LabelID>,
+    label_ids: &Vec<LabelInfo>,
 ) {
     for label in label_ids {
         let (func, nodes) = labels.get_label(*label);
@@ -206,3 +207,4 @@ fn add_device(
         module.functions[func.idx()].device = Some(device.clone());
     }
 }
+*/
-- 
GitLab


From 1d1c44bc26910fa872530d66f1229e3611b6e8e1 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 15:17:16 -0600
Subject: [PATCH 12/46] Propagating labels

---
 hercules_opt/src/editor.rs | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index e79354b7..0d09d64f 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -204,7 +204,7 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             }
 
             // Step 5.0: propagate schedules along sub-edit edges.
-            for (src, dst) in sub_edits {
+            for (src, dst) in sub_edits.iter() {
                 let mut dst_schedules = take(&mut editor.function.schedules[dst.idx()]);
                 for src_schedule in editor.function.schedules[src.idx()].iter() {
                     if !dst_schedules.contains(src_schedule) {
@@ -214,9 +214,16 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 editor.function.schedules[dst.idx()] = dst_schedules;
             }
 
-            // Step 5.1: update and (TODO) propagate labels
+            // Step 5.1: update and propagate labels
             editor.labels.borrow_mut().extend(added_labels);
 
+            // TODO: Is this enough to propagate labels
+            for (src, dst) in sub_edits {
+                let mut dst_labels = take(&mut editor.function.labels[dst.idx()]);
+                dst_labels.extend(editor.function.labels[src.idx()].iter());
+                editor.function.labels[dst.idx()] = dst_labels;
+            }
+
             // Step 6: update the length of mutable_nodes. All added nodes are
             // mutable.
             editor
-- 
GitLab


From 526c88f4d329b7f554de70b7257b965357580782 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 15:21:50 -0600
Subject: [PATCH 13/46] Formatting

---
 hercules_ir/src/build.rs             |  4 +++-
 hercules_ir/src/ir.rs                |  2 +-
 hercules_ir/src/parse.rs             |  2 +-
 hercules_utils/src/stringtab.rs      |  4 +++-
 juno_frontend/src/codegen.rs         | 11 ++++++++---
 juno_frontend/src/labeled_builder.rs |  7 ++++++-
 juno_frontend/src/lib.rs             |  9 +--------
 juno_frontend/src/semant.rs          | 13 ++++++-------
 juno_scheduler/src/compile.rs        |  4 ++--
 juno_scheduler/src/lib.rs            |  4 ++--
 10 files changed, 33 insertions(+), 27 deletions(-)

diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index facb04e6..335f3a75 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -502,7 +502,9 @@ impl<'a> Builder<'a> {
             .nodes
             .push(Node::Start);
         self.module.functions[function.idx()].schedules.push(vec![]);
-        self.module.functions[function.idx()].labels.push(HashSet::new());
+        self.module.functions[function.idx()]
+            .labels
+            .push(HashSet::new());
         NodeBuilder {
             id,
             function_id: function,
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 9a72204c..53cd33c1 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -1,7 +1,7 @@
 extern crate bitvec;
+extern crate hercules_utils;
 extern crate ordered_float;
 extern crate serde;
-extern crate hercules_utils;
 
 use std::collections::HashSet;
 use std::fmt::Write;
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 88bb8105..976e71fe 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -1,5 +1,5 @@
-extern crate nom;
 extern crate hercules_utils;
+extern crate nom;
 
 use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
diff --git a/hercules_utils/src/stringtab.rs b/hercules_utils/src/stringtab.rs
index ae9a9db1..e151b830 100644
--- a/hercules_utils/src/stringtab.rs
+++ b/hercules_utils/src/stringtab.rs
@@ -42,5 +42,7 @@ impl StringTable {
 }
 
 impl Default for StringTable {
-    fn default() -> Self { StringTable::new() }
+    fn default() -> Self {
+        StringTable::new()
+    }
 }
diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 2053bb33..9da530b6 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -31,7 +31,13 @@ struct CodeGenerator<'a> {
 }
 
 impl CodeGenerator<'_> {
-    fn build(Prg { types, funcs, labels }: Prg) -> Module {
+    fn build(
+        Prg {
+            types,
+            funcs,
+            labels,
+        }: Prg,
+    ) -> Module {
         // Identify the functions (by index) which have no type arguments, these are the ones we
         // ask for code to be generated for
         let func_idx =
@@ -271,8 +277,7 @@ impl CodeGenerator<'_> {
             Stmt::BlockStmt { body } => {
                 let mut block = Some(cur_block);
                 for stmt in body.iter() {
-                    let new_block =
-                        self.codegen_stmt(stmt, types, ssa, block.unwrap(), loops);
+                    let new_block = self.codegen_stmt(stmt, types, ssa, block.unwrap(), loops);
                     block = new_block;
                 }
                 block
diff --git a/juno_frontend/src/labeled_builder.rs b/juno_frontend/src/labeled_builder.rs
index 4d31332a..59e9e324 100644
--- a/juno_frontend/src/labeled_builder.rs
+++ b/juno_frontend/src/labeled_builder.rs
@@ -87,7 +87,12 @@ impl<'a> LabeledBuilder<'a> {
         let Some(func) = self.function else {
             panic!("Cannot allocate node without function")
         };
-        let labels = self.builder.get_labels(func, other).iter().cloned().collect::<Vec<_>>();
+        let labels = self
+            .builder
+            .get_labels(func, other)
+            .iter()
+            .cloned()
+            .collect::<Vec<_>>();
         self.allocate_node_labeled(labels)
     }
 
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index 461f251d..279ce819 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -97,14 +97,7 @@ pub fn compile(
     };
     let module = codegen::codegen_program(prg);
 
-    compile_ir(
-        module,
-        verify,
-        x_dot,
-        schedule,
-        output_dir,
-        module_name,
-    )
+    compile_ir(module, verify, x_dot, schedule, output_dir, module_name)
 }
 
 pub fn compile_ir(
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index 5f6ef092..6dc2cb72 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -933,7 +933,11 @@ fn analyze_program(
         }
     }
 
-    Ok(Prg { types, funcs: res, labels })
+    Ok(Prg {
+        types,
+        funcs: res,
+        labels,
+    })
 }
 
 fn process_type_def(
@@ -2370,12 +2374,7 @@ fn process_stmt(
             if !errors.is_empty() {
                 Err(errors)
             } else {
-                Ok((
-                    Stmt::BlockStmt {
-                        body: res,
-                    },
-                    reachable,
-                ))
+                Ok((Stmt::BlockStmt { body: res }, reachable))
             }
         }
         parser::Stmt::CallStmt {
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 746d089d..04a3ab68 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -1,9 +1,9 @@
-use crate::parser::*;
 use crate::ir::*;
+use crate::parser::*;
 
 use hercules_utils::env::Env;
 
-pub enum ScheduleError { }
+pub enum ScheduleError {}
 
 pub fn compile_schedule(sched: OperationList) -> Result<ScheduleStmt, ScheduleError> {
     todo!()
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index d3e5d323..c453e716 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -13,15 +13,15 @@ use self::hercules_ir::ir::*;
 mod parser;
 use crate::parser::lexer;
 
+mod compile;
 mod ir;
 mod labels;
 mod pm;
-mod compile;
 
+use crate::compile::*;
 use crate::ir::*;
 use crate::labels::*;
 use crate::pm::*;
-use crate::compile::*;
 
 // FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
 // - The map from label names to their numbers
-- 
GitLab


From 4fca60fab57c8db6873d9d1aead5574baa975719 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 15:26:26 -0600
Subject: [PATCH 14/46] Clean-up

---
 Cargo.lock                                    | 20 +++++++++----------
 Cargo.toml                                    |  2 +-
 hercules_ir/Cargo.toml                        |  1 -
 hercules_ir/src/ir.rs                         |  2 --
 hercules_ir/src/parse.rs                      |  2 --
 hercules_opt/Cargo.toml                       |  1 -
 hercules_opt/src/editor.rs                    |  2 --
 juno_frontend/Cargo.toml                      |  2 +-
 juno_frontend/src/codegen.rs                  |  2 +-
 juno_frontend/src/labeled_builder.rs          |  4 +---
 juno_frontend/src/lib.rs                      |  1 -
 juno_frontend/src/semant.rs                   |  6 +++---
 juno_scheduler/Cargo.toml                     |  2 +-
 juno_scheduler/src/compile.rs                 |  2 +-
 juno_scheduler/src/lib.rs                     |  2 +-
 {hercules_utils => juno_utils}/.gitignore     |  0
 {hercules_utils => juno_utils}/Cargo.toml     |  4 ++--
 {hercules_utils => juno_utils}/src/env.rs     |  0
 {hercules_utils => juno_utils}/src/lib.rs     |  0
 .../src/stringtab.rs                          |  0
 20 files changed, 21 insertions(+), 34 deletions(-)
 rename {hercules_utils => juno_utils}/.gitignore (100%)
 rename {hercules_utils => juno_utils}/Cargo.toml (80%)
 rename {hercules_utils => juno_utils}/src/env.rs (100%)
 rename {hercules_utils => juno_utils}/src/lib.rs (100%)
 rename {hercules_utils => juno_utils}/src/stringtab.rs (100%)

diff --git a/Cargo.lock b/Cargo.lock
index 058dd3f9..5210d473 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -630,7 +630,6 @@ name = "hercules_ir"
 version = "0.1.0"
 dependencies = [
  "bitvec",
- "hercules_utils",
  "nom",
  "ordered-float",
  "rand",
@@ -645,7 +644,6 @@ dependencies = [
  "either",
  "hercules_cg",
  "hercules_ir",
- "hercules_utils",
  "itertools",
  "ordered-float",
  "postcard",
@@ -653,13 +651,6 @@ dependencies = [
  "take_mut",
 ]
 
-[[package]]
-name = "hercules_utils"
-version = "0.1.0"
-dependencies = [
- "serde",
-]
-
 [[package]]
 name = "hermit-abi"
 version = "0.4.0"
@@ -741,8 +732,8 @@ dependencies = [
  "clap",
  "hercules_ir",
  "hercules_opt",
- "hercules_utils",
  "juno_scheduler",
+ "juno_utils",
  "lrlex",
  "lrpar",
  "num-rational",
@@ -785,7 +776,7 @@ version = "0.0.1"
 dependencies = [
  "cfgrammar",
  "hercules_ir",
- "hercules_utils",
+ "juno_utils",
  "lrlex",
  "lrpar",
 ]
@@ -799,6 +790,13 @@ dependencies = [
  "with_builtin_macros",
 ]
 
+[[package]]
+name = "juno_utils"
+version = "0.1.0"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "kv-log-macro"
 version = "1.0.7"
diff --git a/Cargo.toml b/Cargo.toml
index 0f814021..f147261a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,13 +1,13 @@
 [workspace]
 resolver = "2"
 members = [
-	"hercules_utils",
 	"hercules_cg",
 	"hercules_ir",
 	"hercules_opt",
 	
 	"hercules_tools/hercules_driver",
 
+	"juno_utils",
 	"juno_frontend",
 	"juno_scheduler",
 	"juno_build",
diff --git a/hercules_ir/Cargo.toml b/hercules_ir/Cargo.toml
index 43875fac..6046b4fa 100644
--- a/hercules_ir/Cargo.toml
+++ b/hercules_ir/Cargo.toml
@@ -9,4 +9,3 @@ nom = "*"
 ordered-float = { version = "*", features = ["serde"] }
 bitvec = "*"
 serde = { version = "*", features = ["derive"] }
-hercules_utils = { path = "../hercules_utils" }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 53cd33c1..84284d14 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -1,5 +1,4 @@
 extern crate bitvec;
-extern crate hercules_utils;
 extern crate ordered_float;
 extern crate serde;
 
@@ -13,7 +12,6 @@ use self::bitvec::prelude::*;
 use self::serde::Deserialize;
 use self::serde::Serialize;
 
-use self::hercules_utils::stringtab::StringTable;
 
 use crate::*;
 
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 976e71fe..18ea6eeb 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -1,4 +1,3 @@
-extern crate hercules_utils;
 extern crate nom;
 
 use std::cell::RefCell;
@@ -7,7 +6,6 @@ use std::str::FromStr;
 
 use crate::*;
 
-use self::hercules_utils::stringtab::StringTable;
 
 /*
  * TODO: This parsing code was written before the generic build API was created.
diff --git a/hercules_opt/Cargo.toml b/hercules_opt/Cargo.toml
index 7adc8e15..e1936a97 100644
--- a/hercules_opt/Cargo.toml
+++ b/hercules_opt/Cargo.toml
@@ -13,4 +13,3 @@ postcard = { version = "*", features = ["alloc"] }
 serde = { version = "*", features = ["derive"] }
 hercules_cg = { path = "../hercules_cg" }
 hercules_ir = { path = "../hercules_ir" }
-hercules_utils = { path = "../hercules_utils" }
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 0d09d64f..7dfa948b 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -1,7 +1,6 @@
 extern crate bitvec;
 extern crate either;
 extern crate hercules_ir;
-extern crate hercules_utils;
 extern crate itertools;
 
 use std::cell::{Ref, RefCell};
@@ -14,7 +13,6 @@ use self::either::Either;
 
 use self::hercules_ir::def_use::*;
 use self::hercules_ir::ir::*;
-use self::hercules_utils::stringtab::StringTable;
 
 /*
  * Helper object for editing Hercules functions in a trackable manner. Edits
diff --git a/juno_frontend/Cargo.toml b/juno_frontend/Cargo.toml
index 21aba0e4..ad35b84c 100644
--- a/juno_frontend/Cargo.toml
+++ b/juno_frontend/Cargo.toml
@@ -29,4 +29,4 @@ phf = { version = "0.11", features = ["macros"] }
 hercules_ir = { path = "../hercules_ir" }
 hercules_opt = { path = "../hercules_opt" }
 juno_scheduler = { path = "../juno_scheduler" }
-hercules_utils = { path = "../hercules_utils" }
+juno_utils = { path = "../juno_utils" }
diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 9da530b6..6ce0ddab 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -1,4 +1,4 @@
-use std::collections::{HashMap, HashSet, VecDeque};
+use std::collections::{HashMap, VecDeque};
 
 use hercules_ir::ir;
 use hercules_ir::ir::*;
diff --git a/juno_frontend/src/labeled_builder.rs b/juno_frontend/src/labeled_builder.rs
index 59e9e324..8ae408ed 100644
--- a/juno_frontend/src/labeled_builder.rs
+++ b/juno_frontend/src/labeled_builder.rs
@@ -1,9 +1,7 @@
 use hercules_ir::build::*;
 use hercules_ir::ir::*;
-use juno_scheduler::LabeledStructure;
-use std::collections::{HashMap, HashSet};
 
-use hercules_utils::stringtab::StringTable;
+use juno_utils::stringtab::StringTable;
 
 // A label-tracking code generator which tracks the current function that we're
 // generating code for and the what labels apply to the nodes being created
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index 279ce819..6cffe191 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -13,7 +13,6 @@ extern crate hercules_ir;
 use std::fmt;
 use std::path::Path;
 
-use juno_scheduler::FunctionMap;
 
 pub enum JunoVerify {
     None,
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index 6dc2cb72..07ea9353 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -1,5 +1,5 @@
 extern crate hercules_ir;
-extern crate hercules_utils;
+extern crate juno_utils;
 
 use std::collections::{HashMap, LinkedList};
 use std::fmt;
@@ -20,8 +20,8 @@ use crate::parser::*;
 use crate::types;
 use crate::types::{Either, Type, TypeSolver};
 
-use self::hercules_utils::env::Env;
-use self::hercules_utils::stringtab::StringTable;
+use self::juno_utils::env::Env;
+use self::juno_utils::stringtab::StringTable;
 
 // Definitions and data structures for semantic analysis
 
diff --git a/juno_scheduler/Cargo.toml b/juno_scheduler/Cargo.toml
index f0802422..c41b9310 100644
--- a/juno_scheduler/Cargo.toml
+++ b/juno_scheduler/Cargo.toml
@@ -14,4 +14,4 @@ cfgrammar = "0.13"
 lrlex = "0.13"
 lrpar = "0.13"
 hercules_ir = { path = "../hercules_ir" }
-hercules_utils = { path = "../hercules_utils" }
+juno_utils = { path = "../juno_utils" }
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 04a3ab68..61dc7d6c 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -1,7 +1,7 @@
 use crate::ir::*;
 use crate::parser::*;
 
-use hercules_utils::env::Env;
+use juno_utils::env::Env;
 
 pub enum ScheduleError {}
 
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index c453e716..562997f3 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -1,5 +1,5 @@
 extern crate hercules_ir;
-extern crate hercules_utils;
+extern crate juno_utils;
 
 use std::collections::{HashMap, HashSet};
 use std::fs::File;
diff --git a/hercules_utils/.gitignore b/juno_utils/.gitignore
similarity index 100%
rename from hercules_utils/.gitignore
rename to juno_utils/.gitignore
diff --git a/hercules_utils/Cargo.toml b/juno_utils/Cargo.toml
similarity index 80%
rename from hercules_utils/Cargo.toml
rename to juno_utils/Cargo.toml
index 9b87ba5c..8de3b651 100644
--- a/hercules_utils/Cargo.toml
+++ b/juno_utils/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
-name = "hercules_utils"
+name = "juno_utils"
 version = "0.1.0"
 authors = ["Aaron Councilman <aaronjc4@illinois.edu>"]
 edition = "2021"
 
 [lib]
-name = "hercules_utils"
+name = "juno_utils"
 path = "src/lib.rs"
 
 [dependencies]
diff --git a/hercules_utils/src/env.rs b/juno_utils/src/env.rs
similarity index 100%
rename from hercules_utils/src/env.rs
rename to juno_utils/src/env.rs
diff --git a/hercules_utils/src/lib.rs b/juno_utils/src/lib.rs
similarity index 100%
rename from hercules_utils/src/lib.rs
rename to juno_utils/src/lib.rs
diff --git a/hercules_utils/src/stringtab.rs b/juno_utils/src/stringtab.rs
similarity index 100%
rename from hercules_utils/src/stringtab.rs
rename to juno_utils/src/stringtab.rs
-- 
GitLab


From b337f6f7455cc182b6057aa4189170b025c3a60b Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 8 Jan 2025 18:46:59 -0600
Subject: [PATCH 15/46] Fix schedule interpreter

---
 juno_build/src/lib.rs        |   6 +-
 juno_scheduler/src/ir.rs     |   4 +-
 juno_scheduler/src/labels.rs |  32 +++++++++-
 juno_scheduler/src/lib.rs    |   2 +-
 juno_scheduler/src/pm.rs     | 114 ++++++++++++++++++-----------------
 5 files changed, 93 insertions(+), 65 deletions(-)

diff --git a/juno_build/src/lib.rs b/juno_build/src/lib.rs
index 726e9c3b..62fe8ec3 100644
--- a/juno_build/src/lib.rs
+++ b/juno_build/src/lib.rs
@@ -3,11 +3,9 @@ extern crate hercules_ir;
 use juno_compiler::*;
 
 use std::env::{current_dir, var};
-use std::fmt::Write;
 use std::fs::{create_dir_all, read_to_string};
 use std::path::{Path, PathBuf};
 
-use with_builtin_macros::with_builtin;
 
 // JunoCompiler is used to compile juno files into a library and manifest file appropriately to
 // import the definitions into a rust project via the juno! macro defined below
@@ -182,8 +180,8 @@ impl JunoCompiler {
     // Builds the juno file into a libary and a manifest file.
     pub fn build(self) -> Result<(), String> {
         let JunoCompiler {
-            ir_src_path: ir_src_path,
-            src_path: src_path,
+            ir_src_path,
+            src_path,
             out_path: Some(out_path),
             verify,
             x_dot,
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index b09c08ae..0fdcc4b8 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -34,14 +34,14 @@ pub enum ScheduleExp {
     },
     Field {
         collect: Box<ScheduleExp>,
-        field: String,
+        field: usize,
     },
     RunPass {
         pass: Pass,
         on: Selector,
     },
     Record {
-        fields: Vec<(String, ScheduleExp)>,
+        fields: Vec<(usize, ScheduleExp)>,
     },
     Block {
         body: Vec<ScheduleStmt>,
diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
index ba9a7506..75a746bf 100644
--- a/juno_scheduler/src/labels.rs
+++ b/juno_scheduler/src/labels.rs
@@ -1,7 +1,35 @@
 use hercules_ir::ir::*;
 
+use std::collections::HashMap;
+
 #[derive(Debug, Copy, Clone)]
 pub struct LabelInfo {
-    func: FunctionID,
-    label: LabelID,
+    pub func: FunctionID,
+    pub label: LabelID,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct JunoFunctionID {
+    pub idx: usize,
+}
+
+// From the Juno frontend we collect certain information we need for scheduling it, in particular a
+// map from the function names to a "JunoFunctionID" which can be used to lookup the Hercules
+// FunctionIDs that are definitions of that function (it may be multiple since the same Juno
+// function may be instantiated with multiple difference type variables).
+#[derive(Debug, Clone)]
+pub struct JunoInfo {
+    pub func_names: HashMap<String, JunoFunctionID>,
+    pub func_info: JunoFunctions,
+}
+
+#[derive(Debug, Clone)]
+pub struct JunoFunctions {
+    pub func_ids: Vec<Vec<FunctionID>>,
+}
+
+impl JunoFunctions {
+    pub fn get_function(&self, id: JunoFunctionID) -> &Vec<FunctionID> {
+        &self.func_ids[id.idx]
+    }
 }
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 562997f3..3c68e3d7 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -23,6 +23,7 @@ use crate::ir::*;
 use crate::labels::*;
 use crate::pm::*;
 
+/*
 // FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
 // - The map from label names to their numbers
 // - The name of the function
@@ -57,7 +58,6 @@ pub enum LabeledStructure {
     Branch(NodeID), // If node
 }
 
-/*
 pub fn schedule(module: &Module, info: FunctionMap, schedule: String) -> Result<Vec<Plan>, String> {
     if let Ok(mut file) = File::open(schedule) {
         let mut contents = String::new();
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index e2cca9e8..a8bad404 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,17 +1,17 @@
-/*
 use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
 
-use hercules_utils::env::Env;
+use juno_utils::env::Env;
 
 use std::collections::HashMap;
 
 #[derive(Debug, Clone)]
 pub enum Value {
     Label { labels: Vec<LabelInfo> },
-    Function { func: JunoFunctionID },
-    Record { fields: HashMap<String, Value> },
+    JunoFunction { func: JunoFunctionID },
+    HerculesFunction { func: FunctionID },
+    Record { fields: HashMap<usize, Value> },
 }
 
 #[derive(Debug, Clone)]
@@ -26,16 +26,9 @@ pub fn schedule(
     mut module: Module,
     schedule: ScheduleStmt,
     mut env: Env<usize, Value>,
-    mut labels: ProgramLabels,
     mut functions: JunoFunctions,
 ) -> Result<Module, SchedulerError> {
-    schedule_interpret(
-        &mut module,
-        &schedule,
-        &mut env,
-        &mut labels,
-        &mut functions,
-    )?;
+    schedule_interpret(&mut module, &schedule, &mut env, &mut functions)?;
     Ok(module)
 }
 
@@ -43,7 +36,6 @@ fn schedule_interpret(
     module: &mut Module,
     schedule: &ScheduleStmt,
     env: &mut Env<usize, Value>,
-    labels: &mut ProgramLabels,
     functions: &mut JunoFunctions,
 ) -> Result<(), SchedulerError> {
     match schedule {
@@ -51,16 +43,16 @@ fn schedule_interpret(
         ScheduleStmt::Block { body } => {
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, env, labels, functions)?;
+                schedule_interpret(module, command, env, functions)?;
             }
             env.close_scope();
         }
         ScheduleStmt::Let { var, exp } => {
-            let res = interp_expr(module, exp, env, labels, functions)?;
+            let res = interp_expr(module, exp, env, functions)?;
             env.insert(var.clone(), res);
         }
         ScheduleStmt::Assign { var, exp } => {
-            let res = interp_expr(module, exp, env, labels, functions)?;
+            let res = interp_expr(module, exp, env, functions)?;
             match env.lookup_mut(&var) {
                 None => {
                     return Err(SchedulerError::UndefinedVariable(*var));
@@ -78,11 +70,11 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for label in selection {
-                    match interp_expr(module, label, env, labels, functions)? {
+                    match interp_expr(module, label, env, functions)? {
                         Value::Label { labels: label_ids } => {
-                            add_schedule(module, labels, sched.clone(), &label_ids);
+                            add_schedule(module, sched.clone(), &label_ids);
                         }
-                        Value::Function { .. } => {
+                        Value::JunoFunction { .. } | Value::HerculesFunction { .. } => {
                             return Err(SchedulerError::SemanticError(
                                 "Cannot apply schedule to function".to_string(),
                             ));
@@ -104,15 +96,18 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for func in selection {
-                    match interp_expr(module, func, env, labels, functions)? {
+                    match interp_expr(module, func, env, functions)? {
                         Value::Label { .. } => {
                             return Err(SchedulerError::SemanticError(
                                 "Cannot apply device to label".to_string(),
                             ));
                         }
-                        Value::Function { func } => {
+                        Value::JunoFunction { func } => {
                             add_device(module, functions, device.clone(), func);
                         }
+                        Value::HerculesFunction { func } => {
+                            add_device_hercules(module, device.clone(), func);
+                        }
                         Value::Record { .. } => {
                             return Err(SchedulerError::SemanticError(
                                 "Cannot apply device to record".to_string(),
@@ -130,7 +125,6 @@ fn interp_expr(
     module: &mut Module,
     expr: &ScheduleExp,
     env: &mut Env<usize, Value>,
-    labels: &mut ProgramLabels,
     functions: &mut JunoFunctions,
 ) -> Result<Value, SchedulerError> {
     match expr {
@@ -139,23 +133,30 @@ fn interp_expr(
             Some(v) => Ok(v.clone()),
         },
         ScheduleExp::Field { collect, field } => {
-            match interp_expr(module, collect, env, labels, functions)? {
-                Value::Label { .. } => Err(SchedulerError::UndefinedField(field.clone())),
-                Value::Function { func } => {
-                    let function_info = functions.get_function(func);
-                    let mut labels = vec![];
-                    for (_, func_labels) in function_info {
-                        match func_labels.get(field) {
-                            None => {
-                                return Err(SchedulerError::UndefinedLabel(field.clone()));
-                            }
-                            Some(label) => labels.push(*label),
-                        }
-                    }
-                    Ok(Value::Label { labels })
-                }
+            match interp_expr(module, collect, env, functions)? {
+                Value::Label { .. } => Err(SchedulerError::UndefinedField(
+                    module.labels[*field].clone(),
+                )),
+                Value::JunoFunction { func } => Ok(Value::Label {
+                    labels: functions
+                        .get_function(func)
+                        .iter()
+                        .map(|f| LabelInfo {
+                            func: *f,
+                            label: *field,
+                        })
+                        .collect(),
+                }),
+                Value::HerculesFunction { func } => Ok(Value::Label {
+                    labels: vec![LabelInfo {
+                        func: func,
+                        label: *field,
+                    }],
+                }),
                 Value::Record { fields } => match fields.get(field) {
-                    None => Err(SchedulerError::UndefinedField(field.clone())),
+                    None => Err(SchedulerError::UndefinedField(
+                        module.labels[*field].clone(),
+                    )),
                     Some(v) => Ok(v.clone()),
                 },
             }
@@ -164,35 +165,33 @@ fn interp_expr(
         ScheduleExp::Record { fields } => {
             let mut result = HashMap::new();
             for (field, val) in fields {
-                result.insert(
-                    field.clone(),
-                    interp_expr(module, val, env, labels, functions)?,
-                );
+                result.insert(field.clone(), interp_expr(module, val, env, functions)?);
             }
             Ok(Value::Record { fields: result })
         }
         ScheduleExp::Block { body, res } => {
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, env, labels, functions)?;
+                schedule_interpret(module, command, env, functions)?;
             }
-            let res = interp_expr(module, res, env, labels, functions);
+            let res = interp_expr(module, res, env, functions);
             env.close_scope();
             res
         }
     }
 }
 
-fn add_schedule(
-    module: &mut Module,
-    labels: &ProgramLabels,
-    sched: Schedule,
-    label_ids: &Vec<LabelInfo>,
-) {
-    for label in label_ids {
-        let (func, nodes) = labels.get_label(*label);
-        for node in nodes.clone().into_iter() {
-            module.functions[func.idx()].schedules[node.idx()].push(sched.clone());
+fn add_schedule(module: &mut Module, sched: Schedule, label_ids: &Vec<LabelInfo>) {
+    for LabelInfo { func, label } in label_ids {
+        let nodes = module.functions[func.idx()]
+            .labels
+            .iter()
+            .enumerate()
+            .filter(|(i, ls)| ls.contains(label))
+            .map(|(i, ls)| i)
+            .collect::<Vec<_>>();
+        for node in nodes {
+            module.functions[func.idx()].schedules[node].push(sched.clone());
         }
     }
 }
@@ -203,8 +202,11 @@ fn add_device(
     device: Device,
     func: JunoFunctionID,
 ) {
-    for (func, _) in functions.get_function(func) {
+    for func in functions.get_function(func) {
         module.functions[func.idx()].device = Some(device.clone());
     }
 }
-*/
+
+fn add_device_hercules(module: &mut Module, device: Device, func: FunctionID) {
+    module.functions[func.idx()].device = Some(device);
+}
-- 
GitLab


From 9102ab3616f6939a07cfc2988895db41e458f10f Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 9 Jan 2025 09:17:45 -0600
Subject: [PATCH 16/46] Add creation of fresh labels to editor

---
 hercules_opt/src/editor.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 7dfa948b..d9204573 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -500,6 +500,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
+    // Creates or returns the LabelID for a given label name
     pub fn new_label(&mut self, name: String) -> LabelID {
         let pos = self
             .editor
@@ -517,6 +518,13 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
         }
     }
 
+    // Creates an entirely fresh label and returns its LabelID
+    pub fn fresh_label(&mut self) -> LabelID {
+        let id = self.editor.labels.borrow().len() + self.added_labels.len();
+        self.added_labels.push(format!("#fresh_{}", id));
+        id
+    }
+
     pub fn get_users(&self, id: NodeID) -> impl Iterator<Item = NodeID> + '_ {
         assert!(!self.deleted_nodeids.contains(&id));
         if let Some(users) = self.updated_def_use.get(&id) {
-- 
GitLab


From 7eee369e969702356930af687a188e215f4ccbca Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 9 Jan 2025 10:44:57 -0600
Subject: [PATCH 17/46] Refactor scheduler IR to use strings to separate its
 string table from labels

---
 juno_scheduler/src/ir.rs  |  10 ++--
 juno_scheduler/src/lang.y |   6 +--
 juno_scheduler/src/pm.rs  | 106 +++++++++++++++++++++++---------------
 3 files changed, 73 insertions(+), 49 deletions(-)

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 0fdcc4b8..1cb75513 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -30,18 +30,18 @@ pub enum Selector {
 #[derive(Debug, Clone)]
 pub enum ScheduleExp {
     Variable {
-        var: usize,
+        var: String,
     },
     Field {
         collect: Box<ScheduleExp>,
-        field: usize,
+        field: String,
     },
     RunPass {
         pass: Pass,
         on: Selector,
     },
     Record {
-        fields: Vec<(usize, ScheduleExp)>,
+        fields: Vec<(String, ScheduleExp)>,
     },
     Block {
         body: Vec<ScheduleStmt>,
@@ -67,11 +67,11 @@ pub enum ScheduleStmt {
         body: Vec<ScheduleStmt>,
     },
     Let {
-        var: usize,
+        var: String,
         exp: ScheduleExp,
     },
     Assign {
-        var: usize,
+        var: String,
         exp: ScheduleExp,
     },
     AddSchedule {
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index 74ec8182..69f9739b 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -14,8 +14,8 @@ Schedule -> OperationList
 Stmt -> Stmt
   : 'let' 'ID' '=' Expr ';'
       { Stmt::LetStmt { span: $span, var: span_of_tok($2), expr: $4 } }
-  | Expr '=' Expr ';'
-      { Stmt::AssignStmt { span: $span, lhs: $1, rhs: $3 } }
+  | 'ID' '=' Expr ';'
+      { Stmt::AssignStmt { span: $span, var: span_of_tok($1), rhs: $3 } }
   | Expr ';'
       { Stmt::ExprStmt { span: $span, exp: $1 } }
   | 'fixpoint' FixpointLimit '{' Schedule '}'
@@ -131,7 +131,7 @@ pub enum OperationList {
 
 pub enum Stmt {
   LetStmt    { span: Span, var: Span, expr: Expr },
-  AssignStmt { span: Span, lhs: Expr, rhs: Expr },
+  AssignStmt { span: Span, var: Span, rhs: Expr },
   ExprStmt   { span: Span, exp: Expr },
   Fixpoint   { span: Span, limit: FixpointLimit, body: Box<OperationList> },
   MacroDecl  { span: Span, def: MacroDecl },
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index a8bad404..43c5fb3e 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -3,6 +3,7 @@ use crate::labels::*;
 use hercules_ir::*;
 
 use juno_utils::env::Env;
+use juno_utils::stringtab::StringTable;
 
 use std::collections::HashMap;
 
@@ -11,12 +12,12 @@ pub enum Value {
     Label { labels: Vec<LabelInfo> },
     JunoFunction { func: JunoFunctionID },
     HerculesFunction { func: FunctionID },
-    Record { fields: HashMap<usize, Value> },
+    Record { fields: HashMap<String, Value> },
 }
 
 #[derive(Debug, Clone)]
 pub enum SchedulerError {
-    UndefinedVariable(usize),
+    UndefinedVariable(String),
     UndefinedField(String),
     UndefinedLabel(String),
     SemanticError(String),
@@ -25,16 +26,24 @@ pub enum SchedulerError {
 pub fn schedule(
     mut module: Module,
     schedule: ScheduleStmt,
+    mut stringtab: StringTable,
     mut env: Env<usize, Value>,
     mut functions: JunoFunctions,
 ) -> Result<Module, SchedulerError> {
-    schedule_interpret(&mut module, &schedule, &mut env, &mut functions)?;
+    schedule_interpret(
+        &mut module,
+        &schedule,
+        &mut stringtab,
+        &mut env,
+        &mut functions,
+    )?;
     Ok(module)
 }
 
 fn schedule_interpret(
     module: &mut Module,
     schedule: &ScheduleStmt,
+    stringtab: &mut StringTable,
     env: &mut Env<usize, Value>,
     functions: &mut JunoFunctions,
 ) -> Result<(), SchedulerError> {
@@ -43,19 +52,21 @@ fn schedule_interpret(
         ScheduleStmt::Block { body } => {
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, env, functions)?;
+                schedule_interpret(module, command, stringtab, env, functions)?;
             }
             env.close_scope();
         }
         ScheduleStmt::Let { var, exp } => {
-            let res = interp_expr(module, exp, env, functions)?;
-            env.insert(var.clone(), res);
+            let res = interp_expr(module, exp, stringtab, env, functions)?;
+            let var_id = stringtab.lookup_string(var.clone());
+            env.insert(var_id, res);
         }
         ScheduleStmt::Assign { var, exp } => {
-            let res = interp_expr(module, exp, env, functions)?;
-            match env.lookup_mut(&var) {
+            let res = interp_expr(module, exp, stringtab, env, functions)?;
+            let var_id = stringtab.lookup_string(var.clone());
+            match env.lookup_mut(&var_id) {
                 None => {
-                    return Err(SchedulerError::UndefinedVariable(*var));
+                    return Err(SchedulerError::UndefinedVariable(var.clone()));
                 }
                 Some(val) => {
                     *val = res;
@@ -70,7 +81,7 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for label in selection {
-                    match interp_expr(module, label, env, functions)? {
+                    match interp_expr(module, label, stringtab, env, functions)? {
                         Value::Label { labels: label_ids } => {
                             add_schedule(module, sched.clone(), &label_ids);
                         }
@@ -96,7 +107,7 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for func in selection {
-                    match interp_expr(module, func, env, functions)? {
+                    match interp_expr(module, func, stringtab, env, functions)? {
                         Value::Label { .. } => {
                             return Err(SchedulerError::SemanticError(
                                 "Cannot apply device to label".to_string(),
@@ -124,39 +135,49 @@ fn schedule_interpret(
 fn interp_expr(
     module: &mut Module,
     expr: &ScheduleExp,
+    stringtab: &mut StringTable,
     env: &mut Env<usize, Value>,
     functions: &mut JunoFunctions,
 ) -> Result<Value, SchedulerError> {
     match expr {
-        ScheduleExp::Variable { var } => match env.lookup(var) {
-            None => Err(SchedulerError::UndefinedVariable(var.clone())),
-            Some(v) => Ok(v.clone()),
-        },
+        ScheduleExp::Variable { var } => {
+            let var_id = stringtab.lookup_string(var.clone());
+            match env.lookup(&var_id) {
+                None => Err(SchedulerError::UndefinedVariable(var.clone())),
+                Some(v) => Ok(v.clone()),
+            }
+        }
         ScheduleExp::Field { collect, field } => {
-            match interp_expr(module, collect, env, functions)? {
-                Value::Label { .. } => Err(SchedulerError::UndefinedField(
-                    module.labels[*field].clone(),
-                )),
-                Value::JunoFunction { func } => Ok(Value::Label {
-                    labels: functions
-                        .get_function(func)
-                        .iter()
-                        .map(|f| LabelInfo {
-                            func: *f,
-                            label: *field,
-                        })
-                        .collect(),
-                }),
-                Value::HerculesFunction { func } => Ok(Value::Label {
-                    labels: vec![LabelInfo {
-                        func: func,
-                        label: *field,
-                    }],
-                }),
+            match interp_expr(module, collect, stringtab, env, functions)? {
+                Value::Label { .. } => Err(SchedulerError::UndefinedField(field.clone())),
+                Value::JunoFunction { func } => {
+                    match module.labels.iter().position(|s| s == field) {
+                        None => Err(SchedulerError::UndefinedLabel(field.clone())),
+                        Some(label_id) => Ok(Value::Label {
+                            labels: functions
+                                .get_function(func)
+                                .iter()
+                                .map(|f| LabelInfo {
+                                    func: *f,
+                                    label: label_id,
+                                })
+                                .collect(),
+                        }),
+                    }
+                }
+                Value::HerculesFunction { func } => {
+                    match module.labels.iter().position(|s| s == field) {
+                        None => Err(SchedulerError::UndefinedLabel(field.clone())),
+                        Some(label_id) => Ok(Value::Label {
+                            labels: vec![LabelInfo {
+                                func: func,
+                                label: label_id,
+                            }],
+                        }),
+                    }
+                }
                 Value::Record { fields } => match fields.get(field) {
-                    None => Err(SchedulerError::UndefinedField(
-                        module.labels[*field].clone(),
-                    )),
+                    None => Err(SchedulerError::UndefinedField(field.clone())),
                     Some(v) => Ok(v.clone()),
                 },
             }
@@ -165,16 +186,19 @@ fn interp_expr(
         ScheduleExp::Record { fields } => {
             let mut result = HashMap::new();
             for (field, val) in fields {
-                result.insert(field.clone(), interp_expr(module, val, env, functions)?);
+                result.insert(
+                    field.clone(),
+                    interp_expr(module, val, stringtab, env, functions)?,
+                );
             }
             Ok(Value::Record { fields: result })
         }
         ScheduleExp::Block { body, res } => {
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, env, functions)?;
+                schedule_interpret(module, command, stringtab, env, functions)?;
             }
-            let res = interp_expr(module, res, env, functions);
+            let res = interp_expr(module, res, stringtab, env, functions);
             env.close_scope();
             res
         }
-- 
GitLab


From 9f130ad059448d4d30ca1c5ecb999073289943c3 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 11:18:02 -0600
Subject: [PATCH 18/46] Generate scheduler IR

---
 juno_scheduler/src/compile.rs | 413 +++++++++++++++++++++++++++++++++-
 juno_scheduler/src/ir.rs      |  28 ++-
 juno_scheduler/src/lang.l     |   2 -
 juno_scheduler/src/lang.y     |  18 +-
 juno_scheduler/src/pm.rs      | 178 +++++++++++----
 5 files changed, 570 insertions(+), 69 deletions(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 61dc7d6c..7e324443 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -1,10 +1,413 @@
-use crate::ir::*;
-use crate::parser::*;
+use crate::ir;
+use crate::parser;
 
 use juno_utils::env::Env;
+use juno_utils::stringtab::StringTable;
 
-pub enum ScheduleError {}
+extern crate hercules_ir;
+use self::hercules_ir::ir::{Device, Schedule};
 
-pub fn compile_schedule(sched: OperationList) -> Result<ScheduleStmt, ScheduleError> {
-    todo!()
+use lrlex::DefaultLexerTypes;
+use lrpar::NonStreamingLexer;
+use lrpar::Span;
+
+use std::str::FromStr;
+
+pub enum ScheduleCompilerError {
+    UndefinedMacro(String, Span),
+    NoSuchPass(String, Span),
+    IncorrectArguments {
+        expected: usize,
+        actual: usize,
+        span: Span,
+    },
+}
+
+pub fn compile_schedule(
+    sched: parser::OperationList,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+) -> Result<ir::ScheduleStmt, ScheduleCompilerError> {
+    let mut macrostab = StringTable::new();
+    let mut macros = Env::new();
+
+    Ok(ir::ScheduleStmt::Block {
+        body: compile_ops_as_block(sched, lexer, &mut macrostab, &mut macros)?,
+    })
+}
+
+#[derive(Debug, Clone)]
+struct MacroInfo {
+    params: Vec<String>,
+    selection_name: String,
+    def: ir::ScheduleExp,
+}
+
+impl FromStr for ir::Pass {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "ccp" => Ok(ir::Pass::CCP),
+            "dce" => Ok(ir::Pass::DCE),
+            "delete-uncalled" => Ok(ir::Pass::DeleteUncalled),
+            "fork-guard-elim" => Ok(ir::Pass::ForkGuardElim),
+            "fork-split" => Ok(ir::Pass::ForkSplit),
+            "forkify" => Ok(ir::Pass::Forkify),
+            "gvn" => Ok(ir::Pass::GVN),
+            "infer-schedules" => Ok(ir::Pass::InferSchedules),
+            "inline" => Ok(ir::Pass::Inline),
+            "ip-sroa" | "interprocedural-sroa" => Ok(ir::Pass::InterproceduralSROA),
+            "outline" => Ok(ir::Pass::Outline),
+            "phi-elim" => Ok(ir::Pass::PhiElim),
+            "predication" => Ok(ir::Pass::Predication),
+            "reference" | "legalize-reference-semantics" => {
+                Ok(ir::Pass::LegalizeReferenceSemantics)
+            }
+            "sroa" => Ok(ir::Pass::SROA),
+            "unforkify" => Ok(ir::Pass::Unforkify),
+            "verify" => Ok(ir::Pass::Verify),
+            "xdot" => Ok(ir::Pass::Xdot),
+            _ => Err(s.to_string()),
+        }
+    }
+}
+
+fn compile_ops_as_block(
+    sched: parser::OperationList,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<Vec<ir::ScheduleStmt>, ScheduleCompilerError> {
+    match sched {
+        parser::OperationList::NilStmt() => Ok(vec![]),
+        parser::OperationList::FinalExpr(expr) => {
+            Ok(vec![compile_exp_as_stmt(expr, lexer, macrostab, macros)?])
+        }
+        parser::OperationList::ConsStmt(stmt, ops) => {
+            let mut res = compile_stmt(stmt, lexer, macrostab, macros)?;
+            res.extend(compile_ops_as_block(*ops, lexer, macrostab, macros)?);
+            Ok(res)
+        }
+    }
+}
+
+fn compile_stmt(
+    stmt: parser::Stmt,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<Vec<ir::ScheduleStmt>, ScheduleCompilerError> {
+    match stmt {
+        parser::Stmt::LetStmt { span: _, var, expr } => {
+            let var = lexer.span_str(var).to_string();
+            Ok(vec![ir::ScheduleStmt::Let {
+                var,
+                exp: compile_exp_as_expr(expr, lexer, macrostab, macros)?,
+            }])
+        }
+        parser::Stmt::AssignStmt { span: _, var, rhs } => {
+            let var = lexer.span_str(var).to_string();
+            Ok(vec![ir::ScheduleStmt::Assign {
+                var,
+                exp: compile_exp_as_expr(rhs, lexer, macrostab, macros)?,
+            }])
+        }
+        parser::Stmt::ExprStmt { span: _, exp } => {
+            Ok(vec![compile_exp_as_stmt(exp, lexer, macrostab, macros)?])
+        }
+        parser::Stmt::Fixpoint {
+            span: _,
+            limit,
+            body,
+        } => {
+            let limit = match limit {
+                parser::FixpointLimit::NoLimit { .. } => ir::FixpointLimit::NoLimit(),
+                parser::FixpointLimit::StopAfter { span: _, limit } => {
+                    ir::FixpointLimit::StopAfter(
+                        lexer
+                            .span_str(limit)
+                            .parse()
+                            .expect("Parsing ensures integer"),
+                    )
+                }
+                parser::FixpointLimit::PanicAfter { span: _, limit } => {
+                    ir::FixpointLimit::PanicAfter(
+                        lexer
+                            .span_str(limit)
+                            .parse()
+                            .expect("Parsing ensures integer"),
+                    )
+                }
+                parser::FixpointLimit::PrintIter { .. } => ir::FixpointLimit::PrintIter(),
+            };
+
+            macros.open_scope();
+            let body = compile_ops_as_block(*body, lexer, macrostab, macros);
+            macros.close_scope();
+
+            Ok(vec![ir::ScheduleStmt::Fixpoint {
+                body: Box::new(ir::ScheduleStmt::Block { body: body? }),
+                limit,
+            }])
+        }
+        parser::Stmt::MacroDecl { span: _, def } => {
+            let parser::MacroDecl {
+                name,
+                params,
+                selection_name,
+                def,
+            } = def;
+            let name = lexer.span_str(name).to_string();
+            let macro_id = macrostab.lookup_string(name);
+
+            let selection_name = lexer.span_str(selection_name).to_string();
+
+            let params = params
+                .into_iter()
+                .map(|s| lexer.span_str(s).to_string())
+                .collect();
+
+            let def = compile_macro_def(*def, params, selection_name, lexer, macrostab, macros)?;
+            macros.insert(macro_id, def);
+
+            Ok(vec![])
+        }
+    }
+}
+
+fn compile_exp_as_stmt(
+    expr: parser::Expr,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<ir::ScheduleStmt, ScheduleCompilerError> {
+    match compile_expr(expr, lexer, macrostab, macros)? {
+        ExprResult::Expr(exp) => Ok(ir::ScheduleStmt::Let {
+            var: "_".to_string(),
+            exp,
+        }),
+        ExprResult::Stmt(stm) => Ok(stm),
+    }
+}
+
+fn compile_exp_as_expr(
+    expr: parser::Expr,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<ir::ScheduleExp, ScheduleCompilerError> {
+    match compile_expr(expr, lexer, macrostab, macros)? {
+        ExprResult::Expr(exp) => Ok(exp),
+        ExprResult::Stmt(stm) => Ok(ir::ScheduleExp::Block {
+            body: vec![stm],
+            res: Box::new(ir::ScheduleExp::Record { fields: vec![] }),
+        }),
+    }
+}
+
+enum ExprResult {
+    Expr(ir::ScheduleExp),
+    Stmt(ir::ScheduleStmt),
+}
+
+fn compile_expr(
+    expr: parser::Expr,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<ExprResult, ScheduleCompilerError> {
+    match expr {
+        parser::Expr::Function {
+            span,
+            name,
+            args,
+            selection,
+        } => {
+            let pass: ir::Pass = lexer
+                .span_str(name)
+                .to_lowercase()
+                .parse()
+                .map_err(|s| ScheduleCompilerError::NoSuchPass(s, name))?;
+
+            if args.len() != pass.num_args() {
+                return Err(ScheduleCompilerError::IncorrectArguments {
+                    expected: pass.num_args(),
+                    actual: args.len(),
+                    span,
+                });
+            }
+
+            let mut arg_vals = vec![];
+            for arg in args {
+                arg_vals.push(compile_exp_as_expr(arg, lexer, macrostab, macros)?);
+            }
+
+            let selection = compile_selector(selection, lexer, macrostab, macros)?;
+
+            Ok(ExprResult::Expr(ir::ScheduleExp::RunPass {
+                pass,
+                args: arg_vals,
+                on: selection,
+            }))
+        }
+        parser::Expr::Macro {
+            span,
+            name,
+            args,
+            selection,
+        } => {
+            let name_str = lexer.span_str(name).to_string();
+            let macro_id = macrostab.lookup_string(name_str.clone());
+            let Some(macro_def) = macros.lookup(&macro_id) else {
+                return Err(ScheduleCompilerError::UndefinedMacro(name_str, name));
+            };
+            let macro_def: MacroInfo = macro_def.clone();
+            let MacroInfo {
+                params,
+                selection_name,
+                def,
+            } = macro_def;
+
+            if args.len() != params.len() {
+                return Err(ScheduleCompilerError::IncorrectArguments {
+                    expected: params.len(),
+                    actual: args.len(),
+                    span,
+                });
+            }
+
+            // To initialize the macro's arguments, we have to do this in two steps, we first
+            // evaluate all of the arguments and store them into new variables, using names that
+            // cannot conflict with other values in the program and then we assign those variables
+            // to the macro's parameters; this avoids any shadowing issues, for instance:
+            // macro!(3, x) where macro!'s arguments are named x and y becomes
+            //      let #0 = 3; let #1 = x; let x = #0; let y = #1;
+            // which has the desired semantics, as opposed to
+            //      let x = 3; let y = x;
+            let mut arg_eval = vec![];
+            let mut arg_setters = vec![];
+
+            for (i, (exp, var)) in args.into_iter().zip(params.into_iter()).enumerate() {
+                let tmp = format!("#{}", i);
+                arg_eval.push(ir::ScheduleStmt::Let {
+                    var: tmp.clone(),
+                    exp: compile_exp_as_expr(exp, lexer, macrostab, macros)?,
+                });
+                arg_setters.push(ir::ScheduleStmt::Let {
+                    var,
+                    exp: ir::ScheduleExp::Variable { var: tmp },
+                });
+            }
+
+            // Set the selection
+            arg_eval.push(ir::ScheduleStmt::Let {
+                var: selection_name,
+                exp: ir::ScheduleExp::Selection {
+                    selection: compile_selector(selection, lexer, macrostab, macros)?,
+                },
+            });
+
+            Ok(ExprResult::Expr(ir::ScheduleExp::Block {
+                body: arg_setters,
+                res: Box::new(def),
+            }))
+        }
+        parser::Expr::Variable { span } => {
+            let var = lexer.span_str(span).to_string();
+            Ok(ExprResult::Expr(ir::ScheduleExp::Variable { var }))
+        }
+        parser::Expr::Integer { span } => {
+            let val: usize = lexer.span_str(span).parse().expect("Parsing");
+            Ok(ExprResult::Expr(ir::ScheduleExp::Integer { val }))
+        }
+        parser::Expr::Field {
+            span: _,
+            lhs,
+            field,
+        } => {
+            let field = lexer.span_str(field).to_string();
+            let lhs = compile_exp_as_expr(*lhs, lexer, macrostab, macros)?;
+            Ok(ExprResult::Expr(ir::ScheduleExp::Field {
+                collect: Box::new(lhs),
+                field,
+            }))
+        }
+        parser::Expr::BlockExpr { span: _, body } => {
+            compile_ops_as_expr(*body, lexer, macrostab, macros)
+        }
+        parser::Expr::Record { span: _, fields } => {
+            let mut result = vec![];
+            for (name, expr) in fields {
+                let name = lexer.span_str(name).to_string();
+                let expr = compile_exp_as_expr(expr, lexer, macrostab, macros)?;
+                result.push((name, expr));
+            }
+            Ok(ExprResult::Expr(ir::ScheduleExp::Record { fields: result }))
+        }
+    }
+}
+
+fn compile_ops_as_expr(
+    mut sched: parser::OperationList,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<ExprResult, ScheduleCompilerError> {
+    let mut body = vec![];
+    loop {
+        match sched {
+            parser::OperationList::NilStmt() => {
+                return Ok(ExprResult::Stmt(ir::ScheduleStmt::Block { body }));
+            }
+            parser::OperationList::FinalExpr(expr) => {
+                return Ok(ExprResult::Expr(ir::ScheduleExp::Block {
+                    body,
+                    res: Box::new(compile_exp_as_expr(expr, lexer, macrostab, macros)?),
+                }));
+            }
+            parser::OperationList::ConsStmt(stmt, ops) => {
+                body.extend(compile_stmt(stmt, lexer, macrostab, macros)?);
+                sched = *ops;
+            }
+        }
+    }
+}
+
+fn compile_selector(
+    sel: parser::Selector,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<ir::Selector, ScheduleCompilerError> {
+    match sel {
+        parser::Selector::SelectAll { span: _ } => Ok(ir::Selector::Everything()),
+        parser::Selector::SelectExprs { span: _, exprs } => {
+            let mut res = vec![];
+            for exp in exprs {
+                res.push(compile_exp_as_expr(exp, lexer, macrostab, macros)?);
+            }
+            Ok(ir::Selector::Selection(res))
+        }
+    }
+}
+
+fn compile_macro_def(
+    body: parser::OperationList,
+    params: Vec<String>,
+    selection_name: String,
+    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
+    macrostab: &mut StringTable,
+    macros: &mut Env<usize, MacroInfo>,
+) -> Result<MacroInfo, ScheduleCompilerError> {
+    Ok(MacroInfo {
+        params,
+        selection_name,
+        def: match compile_ops_as_expr(body, lexer, macrostab, macros)? {
+            ExprResult::Expr(expr) => expr,
+            ExprResult::Stmt(stmt) => ir::ScheduleExp::Block {
+                body: vec![stmt],
+                res: Box::new(ir::ScheduleExp::Record { fields: vec![] }),
+            },
+        },
+    })
 }
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 1cb75513..77ea773f 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -19,6 +19,18 @@ pub enum Pass {
     ForkSplit,
     Unforkify,
     InferSchedules,
+    LegalizeReferenceSemantics,
+    Verify,
+    Xdot,
+}
+
+impl Pass {
+    pub fn num_args(&self) -> usize {
+        match self {
+            Pass::Xdot => 1,
+            _ => 0,
+        }
+    }
 }
 
 #[derive(Debug, Clone)]
@@ -32,12 +44,19 @@ pub enum ScheduleExp {
     Variable {
         var: String,
     },
+    Integer {
+        val: usize,
+    },
+    Boolean {
+        val: bool,
+    },
     Field {
         collect: Box<ScheduleExp>,
         field: String,
     },
     RunPass {
         pass: Pass,
+        args: Vec<ScheduleExp>,
         on: Selector,
     },
     Record {
@@ -47,14 +66,19 @@ pub enum ScheduleExp {
         body: Vec<ScheduleStmt>,
         res: Box<ScheduleExp>,
     },
+    // This is used to "box" a selection by evaluating it at one point and then
+    // allowing it to be used as a selector later on
+    Selection {
+        selection: Selector,
+    },
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum FixpointLimit {
     NoLimit(),
     PrintIter(),
-    StopAt(usize),
-    PanicAt(usize),
+    StopAfter(usize),
+    PanicAfter(usize),
 }
 
 #[derive(Debug, Clone)]
diff --git a/juno_scheduler/src/lang.l b/juno_scheduler/src/lang.l
index a8be4e94..a940a267 100644
--- a/juno_scheduler/src/lang.l
+++ b/juno_scheduler/src/lang.l
@@ -13,7 +13,6 @@
 [\n\r]          ;
 
 ,               ","
-:               ":"
 ;               ";"
 =               "="
 @               "@"
@@ -22,7 +21,6 @@
 
 apply           "apply"
 fixpoint        "fixpoint"
-int             "int_keyword"
 let             "let"
 macro           "macro_keyword"
 on              "on"
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index 69f9739b..3f45c8aa 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -90,14 +90,10 @@ MacroDecl -> MacroDecl
       }
   ;
 
-Params -> Vec<(Span, Type)>
-  :                           { vec![] }
-  | 'ID' ':' Type             { vec![(span_of_tok($1), $3)] }
-  | 'ID' ':' Type ',' Params  { snoc((span_of_tok($1), $3), $5) }
-  ;
-
-Type -> Type
-  : 'int_keyword' { Type::Int { span: $span  } }
+Params -> Vec<Span>
+  :                  { vec![] }
+  | 'ID'             { vec![span_of_tok($1)] }
+  | 'ID' ',' Params  { snoc(span_of_tok($1), $3) }
   ;
 
 MacroDef -> OperationList : '{' Schedule '}' { $2 };
@@ -161,11 +157,7 @@ pub enum Selector {
 
 pub struct MacroDecl {
   pub name: Span,
-  pub params: Vec<(Span, Type)>,
+  pub params: Vec<Span>,
   pub selection_name: Span,
   pub def: Box<OperationList>,
 }
-
-pub enum Type {
-  Int { span: Span },
-}
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 43c5fb3e..08fc516a 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -13,6 +13,112 @@ pub enum Value {
     JunoFunction { func: JunoFunctionID },
     HerculesFunction { func: FunctionID },
     Record { fields: HashMap<String, Value> },
+    Everything {},
+    Selection { selection: Vec<Value> },
+    Integer { val: usize },
+    Boolean { val: bool },
+}
+
+enum CodeLocation {
+    Label(LabelInfo),
+    Function(FunctionID),
+}
+
+impl Value {
+    fn is_everything(&self) -> bool {
+        match self {
+            Value::Everything {} => true,
+            _ => false,
+        }
+    }
+
+    fn as_labels(&self) -> Result<Vec<LabelInfo>, SchedulerError> {
+        match self {
+            Value::Label { labels } => Ok(labels.clone()),
+            Value::Selection { selection } => {
+                let mut result = vec![];
+                for val in selection {
+                    result.extend(val.as_labels()?);
+                }
+                Ok(result)
+            }
+            Value::JunoFunction { .. } | Value::HerculesFunction { .. } => Err(
+                SchedulerError::SemanticError("Expected labels, found function".to_string()),
+            ),
+            Value::Record { .. } => Err(SchedulerError::SemanticError(
+                "Expected labels, found record".to_string(),
+            )),
+            Value::Everything {} => Err(SchedulerError::SemanticError(
+                "Expected labels, found everything".to_string(),
+            )),
+            Value::Integer { .. } => Err(SchedulerError::SemanticError(
+                "Expected labels, found integer".to_string(),
+            )),
+            Value::Boolean { .. } => Err(SchedulerError::SemanticError(
+                "Expected labels, found boolean".to_string(),
+            )),
+        }
+    }
+
+    fn as_functions(&self, funcs: &JunoFunctions) -> Result<Vec<FunctionID>, SchedulerError> {
+        match self {
+            Value::JunoFunction { func } => Ok(funcs.get_function(*func).clone()),
+            Value::HerculesFunction { func } => Ok(vec![*func]),
+            Value::Selection { selection } => {
+                let mut result = vec![];
+                for val in selection {
+                    result.extend(val.as_functions(funcs)?);
+                }
+                Ok(result)
+            }
+            Value::Label { .. } => Err(SchedulerError::SemanticError(
+                "Expected functions, found label".to_string(),
+            )),
+            Value::Record { .. } => Err(SchedulerError::SemanticError(
+                "Expected functions, found record".to_string(),
+            )),
+            Value::Everything {} => Err(SchedulerError::SemanticError(
+                "Expected functions, found everything".to_string(),
+            )),
+            Value::Integer { .. } => Err(SchedulerError::SemanticError(
+                "Expected functions, found integer".to_string(),
+            )),
+            Value::Boolean { .. } => Err(SchedulerError::SemanticError(
+                "Expected functions, found boolean".to_string(),
+            )),
+        }
+    }
+
+    fn as_locations(&self, funcs: &JunoFunctions) -> Result<Vec<CodeLocation>, SchedulerError> {
+        match self {
+            Value::Label { labels } => Ok(labels.iter().map(|l| CodeLocation::Label(*l)).collect()),
+            Value::JunoFunction { func } => Ok(funcs
+                .get_function(*func)
+                .iter()
+                .map(|f| CodeLocation::Function(*f))
+                .collect()),
+            Value::HerculesFunction { func } => Ok(vec![CodeLocation::Function(*func)]),
+            Value::Selection { selection } => {
+                let mut result = vec![];
+                for val in selection {
+                    result.extend(val.as_locations(funcs)?);
+                }
+                Ok(result)
+            }
+            Value::Record { .. } => Err(SchedulerError::SemanticError(
+                "Expected code locations, found record".to_string(),
+            )),
+            Value::Everything {} => {
+                panic!("Internal error, check is_everything() before using as_functions()")
+            }
+            Value::Integer { .. } => Err(SchedulerError::SemanticError(
+                "Expected code locations, found integer".to_string(),
+            )),
+            Value::Boolean { .. } => Err(SchedulerError::SemanticError(
+                "Expected code locations, found boolean".to_string(),
+            )),
+        }
+    }
 }
 
 #[derive(Debug, Clone)]
@@ -81,21 +187,8 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for label in selection {
-                    match interp_expr(module, label, stringtab, env, functions)? {
-                        Value::Label { labels: label_ids } => {
-                            add_schedule(module, sched.clone(), &label_ids);
-                        }
-                        Value::JunoFunction { .. } | Value::HerculesFunction { .. } => {
-                            return Err(SchedulerError::SemanticError(
-                                "Cannot apply schedule to function".to_string(),
-                            ));
-                        }
-                        Value::Record { .. } => {
-                            return Err(SchedulerError::SemanticError(
-                                "Cannot apply schedule to record".to_string(),
-                            ));
-                        }
-                    }
+                    let label = interp_expr(module, label, stringtab, env, functions)?;
+                    add_schedule(module, sched.clone(), label.as_labels()?);
                 }
             }
         },
@@ -107,24 +200,8 @@ fn schedule_interpret(
             }
             Selector::Selection(selection) => {
                 for func in selection {
-                    match interp_expr(module, func, stringtab, env, functions)? {
-                        Value::Label { .. } => {
-                            return Err(SchedulerError::SemanticError(
-                                "Cannot apply device to label".to_string(),
-                            ));
-                        }
-                        Value::JunoFunction { func } => {
-                            add_device(module, functions, device.clone(), func);
-                        }
-                        Value::HerculesFunction { func } => {
-                            add_device_hercules(module, device.clone(), func);
-                        }
-                        Value::Record { .. } => {
-                            return Err(SchedulerError::SemanticError(
-                                "Cannot apply device to record".to_string(),
-                            ));
-                        }
-                    }
+                    let func = interp_expr(module, func, stringtab, env, functions)?;
+                    add_device(module, device.clone(), func.as_functions(functions)?);
                 }
             }
         },
@@ -147,9 +224,15 @@ fn interp_expr(
                 Some(v) => Ok(v.clone()),
             }
         }
+        ScheduleExp::Integer { val } => Ok(Value::Integer { val: *val }),
+        ScheduleExp::Boolean { val } => Ok(Value::Boolean { val: *val }),
         ScheduleExp::Field { collect, field } => {
             match interp_expr(module, collect, stringtab, env, functions)? {
-                Value::Label { .. } => Err(SchedulerError::UndefinedField(field.clone())),
+                Value::Label { .. }
+                | Value::Selection { .. }
+                | Value::Everything { .. }
+                | Value::Integer { .. }
+                | Value::Boolean { .. } => Err(SchedulerError::UndefinedField(field.clone())),
                 Value::JunoFunction { func } => {
                     match module.labels.iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
@@ -202,16 +285,26 @@ fn interp_expr(
             env.close_scope();
             res
         }
+        ScheduleExp::Selection { selection } => match selection {
+            Selector::Everything() => Ok(Value::Everything {}),
+            Selector::Selection(selection) => {
+                let mut values = vec![];
+                for e in selection {
+                    values.push(interp_expr(module, e, stringtab, env, functions)?);
+                }
+                Ok(Value::Selection { selection: values })
+            }
+        },
     }
 }
 
-fn add_schedule(module: &mut Module, sched: Schedule, label_ids: &Vec<LabelInfo>) {
+fn add_schedule(module: &mut Module, sched: Schedule, label_ids: Vec<LabelInfo>) {
     for LabelInfo { func, label } in label_ids {
         let nodes = module.functions[func.idx()]
             .labels
             .iter()
             .enumerate()
-            .filter(|(i, ls)| ls.contains(label))
+            .filter(|(i, ls)| ls.contains(&label))
             .map(|(i, ls)| i)
             .collect::<Vec<_>>();
         for node in nodes {
@@ -220,17 +313,8 @@ fn add_schedule(module: &mut Module, sched: Schedule, label_ids: &Vec<LabelInfo>
     }
 }
 
-fn add_device(
-    module: &mut Module,
-    functions: &JunoFunctions,
-    device: Device,
-    func: JunoFunctionID,
-) {
-    for func in functions.get_function(func) {
+fn add_device(module: &mut Module, device: Device, funcs: Vec<FunctionID>) {
+    for func in funcs {
         module.functions[func.idx()].device = Some(device.clone());
     }
 }
-
-fn add_device_hercules(module: &mut Module, device: Device, func: FunctionID) {
-    module.functions[func.idx()].device = Some(device);
-}
-- 
GitLab


From 41a5893eab81089f3340d3264d8a71ea158ac8ba Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 13:06:26 -0600
Subject: [PATCH 19/46] Collect Juno Information during codegen

---
 juno_frontend/src/codegen.rs  |  18 +-
 juno_frontend/src/lib.rs      |   3 +-
 juno_scheduler/src/compile.rs |  46 +++-
 juno_scheduler/src/labels.rs  |  21 ++
 juno_scheduler/src/lib.rs     | 382 ++++++++--------------------------
 juno_scheduler/src/pm.rs      |  12 ++
 6 files changed, 176 insertions(+), 306 deletions(-)

diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 6ce0ddab..5cfef27d 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -9,10 +9,12 @@ use crate::semant::{BinaryOp, Expr, Function, Literal, Prg, Stmt, UnaryOp};
 use crate::ssa::SSA;
 use crate::types::{Either, Primitive, TypeSolver, TypeSolverInst};
 
+use juno_scheduler::labels::*;
+
 // Loop info is a stack of the loop levels, recording the latch and exit block of each
 type LoopInfo = Vec<(NodeID, NodeID)>;
 
-pub fn codegen_program(prg: Prg) -> Module {
+pub fn codegen_program(prg: Prg) -> (Module, JunoInfo) {
     CodeGenerator::build(prg)
 }
 
@@ -28,6 +30,10 @@ struct CodeGenerator<'a> {
     // type-solving instantiation (account for the type parameters), the function id, and the entry
     // block id
     worklist: VecDeque<(usize, TypeSolverInst<'a>, FunctionID, NodeID)>,
+
+    // The JunoInfo needed for scheduling, which tracks the Juno function names and their
+    // associated FunctionIDs.
+    juno_info: JunoInfo,
 }
 
 impl CodeGenerator<'_> {
@@ -37,7 +43,7 @@ impl CodeGenerator<'_> {
             funcs,
             labels,
         }: Prg,
-    ) -> Module {
+    ) -> (Module, JunoInfo) {
         // Identify the functions (by index) which have no type arguments, these are the ones we
         // ask for code to be generated for
         let func_idx =
@@ -46,6 +52,8 @@ impl CodeGenerator<'_> {
                 .enumerate()
                 .filter_map(|(i, f)| if f.num_type_args == 0 { Some(i) } else { None });
 
+        let juno_info = JunoInfo::new(funcs.iter().map(|f| f.name.clone()));
+
         let mut codegen = CodeGenerator {
             builder: LabeledBuilder::create(labels),
             types: &types,
@@ -53,6 +61,7 @@ impl CodeGenerator<'_> {
             uid: 0,
             functions: HashMap::new(),
             worklist: VecDeque::new(),
+            juno_info,
         };
 
         // Add the identifed functions to the list to code-gen
@@ -63,7 +72,7 @@ impl CodeGenerator<'_> {
         codegen.finish()
     }
 
-    fn finish(mut self) -> Module {
+    fn finish(mut self) -> (Module, JunoInfo) {
         while !self.worklist.is_empty() {
             let (idx, mut type_inst, func, entry) = self.worklist.pop_front().unwrap();
             self.builder.set_function(func);
@@ -77,9 +86,10 @@ impl CodeGenerator<'_> {
             uid: _,
             functions,
             worklist: _,
+            juno_info
         } = self;
 
-        builder.finish()
+        (builder.finish(), juno_info)
     }
 
     fn get_function(&mut self, func_idx: usize, ty_args: Vec<TypeID>) -> FunctionID {
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index 6cffe191..6e18f5e8 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -9,6 +9,7 @@ mod ssa;
 mod types;
 
 extern crate hercules_ir;
+extern crate juno_scheduler;
 
 use std::fmt;
 use std::path::Path;
@@ -94,7 +95,7 @@ pub fn compile(
             return Err(ErrorMessage::SemanticError(msg));
         }
     };
-    let module = codegen::codegen_program(prg);
+    let (module, juno_info) = codegen::codegen_program(prg);
 
     compile_ir(module, verify, x_dot, schedule, output_dir, module_name)
 }
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 7e324443..f56dbaff 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -11,18 +11,47 @@ use lrlex::DefaultLexerTypes;
 use lrpar::NonStreamingLexer;
 use lrpar::Span;
 
+use std::fmt;
 use std::str::FromStr;
 
+type Location = ((usize, usize), (usize, usize));
+
 pub enum ScheduleCompilerError {
-    UndefinedMacro(String, Span),
-    NoSuchPass(String, Span),
+    UndefinedMacro(String, Location),
+    NoSuchPass(String, Location),
     IncorrectArguments {
         expected: usize,
         actual: usize,
-        span: Span,
+        loc: Location,
     },
 }
 
+impl fmt::Display for ScheduleCompilerError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ScheduleCompilerError::UndefinedMacro(name, loc) => write!(
+                f,
+                "({}, {}) -- ({}, {}): Undefined macro '{}'",
+                loc.0 .0, loc.0 .1, loc.1 .0, loc.1 .1, name
+            ),
+            ScheduleCompilerError::NoSuchPass(name, loc) => write!(
+                f,
+                "({}, {}) -- ({}, {}): Undefined pass '{}'",
+                loc.0 .0, loc.0 .1, loc.1 .0, loc.1 .1, name
+            ),
+            ScheduleCompilerError::IncorrectArguments {
+                expected,
+                actual,
+                loc,
+            } => write!(
+                f,
+                "({}, {}) -- ({}, {}): Expected {} arguments, found {}",
+                loc.0 .0, loc.0 .1, loc.1 .0, loc.1 .1, expected, actual
+            ),
+        }
+    }
+}
+
 pub fn compile_schedule(
     sched: parser::OperationList,
     lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
@@ -227,13 +256,13 @@ fn compile_expr(
                 .span_str(name)
                 .to_lowercase()
                 .parse()
-                .map_err(|s| ScheduleCompilerError::NoSuchPass(s, name))?;
+                .map_err(|s| ScheduleCompilerError::NoSuchPass(s, lexer.line_col(name)))?;
 
             if args.len() != pass.num_args() {
                 return Err(ScheduleCompilerError::IncorrectArguments {
                     expected: pass.num_args(),
                     actual: args.len(),
-                    span,
+                    loc: lexer.line_col(span),
                 });
             }
 
@@ -259,7 +288,10 @@ fn compile_expr(
             let name_str = lexer.span_str(name).to_string();
             let macro_id = macrostab.lookup_string(name_str.clone());
             let Some(macro_def) = macros.lookup(&macro_id) else {
-                return Err(ScheduleCompilerError::UndefinedMacro(name_str, name));
+                return Err(ScheduleCompilerError::UndefinedMacro(
+                    name_str,
+                    lexer.line_col(name),
+                ));
             };
             let macro_def: MacroInfo = macro_def.clone();
             let MacroInfo {
@@ -272,7 +304,7 @@ fn compile_expr(
                 return Err(ScheduleCompilerError::IncorrectArguments {
                     expected: params.len(),
                     actual: args.len(),
-                    span,
+                    loc: lexer.line_col(span),
                 });
             }
 
diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
index 75a746bf..178983b1 100644
--- a/juno_scheduler/src/labels.rs
+++ b/juno_scheduler/src/labels.rs
@@ -13,6 +13,12 @@ pub struct JunoFunctionID {
     pub idx: usize,
 }
 
+impl JunoFunctionID {
+    pub fn new(idx: usize) -> Self {
+        JunoFunctionID { idx }
+    }
+}
+
 // From the Juno frontend we collect certain information we need for scheduling it, in particular a
 // map from the function names to a "JunoFunctionID" which can be used to lookup the Hercules
 // FunctionIDs that are definitions of that function (it may be multiple since the same Juno
@@ -28,6 +34,21 @@ pub struct JunoFunctions {
     pub func_ids: Vec<Vec<FunctionID>>,
 }
 
+impl JunoInfo {
+    pub fn new<I>(funcs: I) -> Self 
+    where I: Iterator<Item = String> {
+        let mut func_names = HashMap::new();
+        let mut func_ids = vec![];
+
+        for (idx, name) in funcs.enumerate() {
+            func_names.insert(name, JunoFunctionID::new(idx));
+            func_ids.push(vec![]);
+        }
+
+        JunoInfo { func_names, func_info: JunoFunctions { func_ids } }
+    }
+}
+
 impl JunoFunctions {
     pub fn get_function(&self, id: JunoFunctionID) -> &Vec<FunctionID> {
         &self.func_ids[id.idx]
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 3c68e3d7..719497cd 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -9,13 +9,15 @@ use lrlex::DefaultLexerTypes;
 use lrpar::NonStreamingLexer;
 
 use self::hercules_ir::ir::*;
+use self::juno_utils::env::Env;
+use self::juno_utils::stringtab::StringTable;
 
 mod parser;
 use crate::parser::lexer;
 
 mod compile;
 mod ir;
-mod labels;
+pub mod labels;
 mod pm;
 
 use crate::compile::*;
@@ -23,43 +25,12 @@ use crate::ir::*;
 use crate::labels::*;
 use crate::pm::*;
 
-/*
-// FunctionMap tracks a map from function numbers (produced by semantic analysis) to a tuple of
-// - The map from label names to their numbers
-// - The name of the function
-// - A list of the instances of the function tracking
-//   + The instantiated type variables
-//   + The resulting FunctionID
-//   + A list of each label, tracking the structure at the label and a set of
-//     the labels which are its descendants
-//   + A map from NodeID to the innermost label containing it
-// This is the core data structure provided from code generation, along with the
-// module
-pub type FunctionMap = HashMap<
-    usize,
-    (
-        HashMap<String, usize>,
-        String,
-        Vec<(
-            Vec<TypeID>,
-            FunctionID,
-            Vec<(LabeledStructure, HashSet<usize>)>,
-            HashMap<NodeID, usize>,
-        )>,
-    ),
->;
-// LabeledStructure represents structures from the source code and where they
-// exist in the IR
-#[derive(Copy, Clone)]
-pub enum LabeledStructure {
-    Nothing(),
-    Expression(NodeID),
-    Loop(NodeID),   // Header
-    Branch(NodeID), // If node
-}
-
-pub fn schedule(module: &Module, info: FunctionMap, schedule: String) -> Result<Vec<Plan>, String> {
-    if let Ok(mut file) = File::open(schedule) {
+pub fn schedule_juno(
+    module: Module,
+    juno_info: JunoInfo,
+    sched_filename: String,
+) -> Result<Module, String> {
+    if let Ok(mut file) = File::open(sched_filename) {
         let mut contents = String::new();
         if let Ok(_) = file.read_to_string(&mut contents) {
             let lexerdef = lexer::lexerdef();
@@ -68,284 +39,107 @@ pub fn schedule(module: &Module, info: FunctionMap, schedule: String) -> Result<
 
             if errs.is_empty() {
                 match res {
-                    None => Err(format!("No parse errors, but no parsing failed")),
+                    None => Err(format!("No parse errors, but parsing the schedule failed")),
                     Some(schd) => {
-                        let mut sched = generate_schedule(module, info, schd, &lexer)?;
-                        let mut schedules = vec![];
-                        for i in 0..sched.len() {
-                            schedules.push(sched.remove(&FunctionID::new(i)).unwrap());
+                        let schd = compile_schedule(schd, &lexer)
+                            .map_err(|e| format!("Schedule Error: {}", e))?;
+
+                        let mut strings = StringTable::new();
+                        let mut env = Env::new();
+
+                        env.open_scope();
+
+                        let JunoInfo {
+                            func_names,
+                            func_info,
+                        } = juno_info;
+                        for (func_name, func_id) in func_names {
+                            let func_name = strings.lookup_string(func_name);
+                            env.insert(func_name, Value::JunoFunction { func: func_id });
                         }
-                        Ok(schedules)
+
+                        env.open_scope();
+                        schedule(module, schd, strings, env, func_info)
+                            .map_err(|e| format!("Scheduling Error: {}", e))
                     }
                 }
             } else {
                 Err(errs
                     .iter()
-                    .map(|e| format!("Syntax Error: {}", e.pp(&lexer, &parser::token_epp)))
+                    .map(|e| {
+                        format!(
+                            "Schedule Syntax Error: {}",
+                            e.pp(&lexer, &parser::token_epp)
+                        )
+                    })
                     .collect::<Vec<_>>()
                     .join("\n"))
             }
         } else {
-            Err(format!("Unable to read input file"))
+            Err(format!("Unable to read schedule"))
         }
     } else {
-        Err(format!("Unable to open input file"))
-    }
-}
-
-// a plan that tracks additional information useful while we construct the
-// schedule
-struct TempPlan {
-    schedules: Vec<Vec<Schedule>>,
-    // we track both the partition each node is in and what labeled caused us
-    // to assign that partition
-    partitions: Vec<(usize, PartitionNumber)>,
-    partition_devices: Vec<Vec<Device>>,
-}
-type PartitionNumber = usize;
-
-impl Into<Plan> for TempPlan {
-    fn into(self) -> Plan {
-        let num_partitions = self.partition_devices.len();
-        Plan {
-            schedules: self.schedules,
-            partitions: self
-                .partitions
-                .into_iter()
-                .map(|(_, n)| PartitionID::new(n))
-                .collect::<Vec<_>>(),
-            partition_devices: self
-                .partition_devices
-                .into_iter()
-                .map(|mut d| {
-                    if d.len() != 1 {
-                        panic!("Partition with multiple devices")
-                    } else {
-                        d.pop().unwrap()
-                    }
-                })
-                .collect::<Vec<_>>(),
-            num_partitions: num_partitions,
-        }
+        Err(format!("Unable to open schedule"))
     }
 }
 
-fn generate_schedule(
-    module: &Module,
-    info: FunctionMap,
-    schedule: Vec<parser::FuncDirectives>,
-    lexer: &dyn NonStreamingLexer<DefaultLexerTypes<u32>>,
-) -> Result<HashMap<FunctionID, Plan>, String> {
-    let mut res: HashMap<FunctionID, TempPlan> = HashMap::new();
-
-    // We initialize every node in every function as not having any schedule
-    // and being in the default partition which is a CPU-only partition
-    // (a result of label 0)
-    for (_, (_, _, func_insts)) in info.iter() {
-        for (_, func_id, _, _) in func_insts.iter() {
-            let num_nodes = module.functions[func_id.idx()].nodes.len();
-            res.insert(
-                *func_id,
-                TempPlan {
-                    schedules: vec![vec![]; num_nodes],
-                    partitions: vec![(0, 0); num_nodes],
-                    partition_devices: vec![vec![Device::CPU]],
-                },
-            );
-        }
-    }
-
-    // Construct a map from function names to function numbers
-    let mut function_names: HashMap<String, usize> = HashMap::new();
-    for (num, (_, nm, _)) in info.iter() {
-        function_names.insert(nm.clone(), *num);
-    }
-    // Make the map immutable
-    let function_names = function_names;
-
-    for parser::FuncDirectives {
-        span: _,
-        func,
-        directives,
-    } in schedule
-    {
-        // Identify the function
-        let parser::Func {
-            span: _,
-            name: func_name,
-        } = func;
-        let name = lexer.span_str(func_name).to_string();
-        let func_num = match function_names.get(&name) {
-            Some(num) => num,
-            None => {
-                return Err(format!("Function {} is undefined", name));
-            }
-        };
-
-        // Identify label information
-        let (label_map, _, func_inst) = info.get(func_num).unwrap();
-        let get_label_num = |label_span| {
-            let label_name = lexer.span_str(label_span).to_string();
-            match label_map.get(&label_name) {
-                Some(num) => Ok(*num),
-                None => Err(format!("Label {} undefined in {}", label_name, name)),
-            }
-        };
-
-        // Process the partitioning and scheduling directives for each instance
-        // of the function
-        for (_, func_id, label_info, node_labels) in func_inst {
-            let func_info = res.get_mut(func_id).unwrap();
-
-            for directive in &directives {
-                match directive {
-                    parser::Directive::Partition {
-                        span: _,
-                        labels,
-                        devices,
-                    } => {
-                        // Setup the new partition
-                        let partition_num = func_info.partition_devices.len();
-                        let mut partition_devices = vec![];
-
-                        for parser::Device { span: _, name } in devices {
-                            let device_name = lexer.span_str(*name).to_string();
-                            if device_name == "cpu" {
-                                partition_devices.push(Device::CPU);
-                            } else if device_name == "gpu" {
-                                partition_devices.push(Device::GPU);
-                            } else {
-                                return Err(format!("Invalid device {}", device_name));
-                            }
-                        }
-
-                        func_info.partition_devices.push(partition_devices);
-
-                        for label in labels {
-                            let label_num = get_label_num(*label)?;
-                            let descendants = &label_info[label_num].1;
+pub fn schedule_hercules(module: Module, sched_filename: String) -> Result<Module, String> {
+    if let Ok(mut file) = File::open(sched_filename) {
+        let mut contents = String::new();
+        if let Ok(_) = file.read_to_string(&mut contents) {
+            let lexerdef = lexer::lexerdef();
+            let lexer = lexerdef.lexer(&contents);
+            let (res, errs) = parser::parse(&lexer);
 
-                            node_labels
-                                .iter()
-                                .filter_map(|(node, label)| {
-                                    if *label == label_num || descendants.contains(label) {
-                                        Some(node.idx())
-                                    } else {
-                                        None
-                                    }
-                                })
-                                .for_each(|node| {
-                                    let node_part: &mut (usize, PartitionNumber) =
-                                        &mut func_info.partitions[node];
-                                    if !descendants.contains(&node_part.0) {
-                                        *node_part = (label_num, partition_num);
-                                    }
-                                });
-                        }
-                    }
-                    parser::Directive::Schedule {
-                        span: _,
-                        command,
-                        args,
-                    } => {
-                        let command = lexer.span_str(*command).to_string();
-                        if command == "parallelize" {
-                            for label in args {
-                                let label_num = get_label_num(*label)?;
-                                match label_info[label_num].0 {
-                                    LabeledStructure::Loop(header) => {
-                                        func_info.schedules[header.idx()]
-                                            .push(Schedule::ParallelReduce);
-                                    }
-                                    _ => {
-                                        return Err(format!(
-                                            "Cannot parallelize {}, not a loop",
-                                            lexer.span_str(*label)
-                                        ));
-                                    }
-                                }
-                            }
-                        } else if command == "vectorize" {
-                            for label in args {
-                                let label_num = get_label_num(*label)?;
-                                match label_info[label_num].0 {
-                                    LabeledStructure::Loop(header) => {
-                                        // FIXME: Take the factor as part of schedule
-                                        func_info.schedules[header.idx()]
-                                            .push(Schedule::Vectorizable(8));
-                                    }
-                                    _ => {
-                                        return Err(format!(
-                                            "Cannot vectorize {}, not a loop",
-                                            lexer.span_str(*label)
-                                        ));
-                                    }
-                                }
-                            }
-                        } else {
-                            return Err(format!("Command {} undefined", command));
+            if errs.is_empty() {
+                match res {
+                    None => Err(format!("No parse errors, but parsing the schedule failed")),
+                    Some(schd) => {
+                        let schd = compile_schedule(schd, &lexer)
+                            .map_err(|e| format!("Schedule Error: {}", e))?;
+
+                        let mut strings = StringTable::new();
+                        let mut env = Env::new();
+
+                        env.open_scope();
+
+                        for (idx, func) in module.functions.iter().enumerate() {
+                            let func_name = strings.lookup_string(func.name.clone());
+                            env.insert(
+                                func_name,
+                                Value::HerculesFunction {
+                                    func: FunctionID::new(idx),
+                                },
+                            );
                         }
-                    }
-                }
-            }
-            /*
-
-            /*
-            for parser::Command { span : _, name : command_name,
-                                  args : command_args } in commands.iter() {
-                if command_args.len() != 0 { todo!("Command arguments not supported") }
-
-                let command = lexer.span_str(*command_name).to_string();
-                if command == "cpu" || command == "gpu" {
-                    let partition = res.get(func_id).unwrap()
-                                       .partition_devices.len();
-                    res.get_mut(func_id).unwrap().partition_devices.push(
-                        if command == "cpu" { Device::CPU }
-                        else { Device::GPU });
 
-                    node_labels.iter()
-                               .filter_map(|(node, label)|
-                                   if label_num == *label
-                                   || label_info[label_num].1.contains(&label) {
-                                       Some(node.idx())
-                                   } else {
-                                       None
-                                   })
-                               .for_each(|node| {
-                                     let node_part : &mut (usize, PartitionNumber) =
-                                        &mut res.get_mut(func_id).unwrap().partitions[node];
-                                     if !label_info[label_num].1.contains(&node_part.0) {
-                                        *node_part = (label_num, partition);
-                                     }});
-                } else if command == "parallel" || command == "vectorize" {
-                    match label_info[label_num].0 {
-                        LabeledStructure::Loop(header) => {
-                            res.get_mut(func_id).unwrap()
-                                .schedules[header.idx()]
-                                .push(if command == "parallel" {
-                                    Schedule::ParallelReduce
-                                } else {
-                                    Schedule::Vectorize
-                                });
-                        },
-                        _ => {
-                            return Err(format!("Cannot parallelize, not a loop"));
-                        },
+                        env.open_scope();
+                        schedule(
+                            module,
+                            schd,
+                            strings,
+                            env,
+                            JunoFunctions { func_ids: vec![] },
+                        )
+                        .map_err(|e| format!("Scheduling Error: {}", e))
                     }
-                } else {
-                    return Err(format!("Command {} undefined", command));
                 }
+            } else {
+                Err(errs
+                    .iter()
+                    .map(|e| {
+                        format!(
+                            "Schedule Syntax Error: {}",
+                            e.pp(&lexer, &parser::token_epp)
+                        )
+                    })
+                    .collect::<Vec<_>>()
+                    .join("\n"))
             }
-            */
-
-            func_info.partition_devices.push(partition_devices);
-            */
+        } else {
+            Err(format!("Unable to read schedule"))
         }
+    } else {
+        Err(format!("Unable to open schedule"))
     }
-
-    Ok(res
-        .into_iter()
-        .map(|(f, p)| (f, p.into()))
-        .collect::<HashMap<_, _>>())
 }
-*/
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 08fc516a..89f9fb3f 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -6,6 +6,7 @@ use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
 
 use std::collections::HashMap;
+use std::fmt;
 
 #[derive(Debug, Clone)]
 pub enum Value {
@@ -129,6 +130,17 @@ pub enum SchedulerError {
     SemanticError(String),
 }
 
+impl fmt::Display for SchedulerError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            SchedulerError::UndefinedVariable(nm) => write!(f, "Undefined variable '{}'", nm),
+            SchedulerError::UndefinedField(nm) => write!(f, "No field '{}'", nm),
+            SchedulerError::UndefinedLabel(nm) => write!(f, "No label '{}'", nm),
+            SchedulerError::SemanticError(msg) => write!(f, "{}", msg),
+        }
+    }
+}
+
 pub fn schedule(
     mut module: Module,
     schedule: ScheduleStmt,
-- 
GitLab


From d30f178abaa5c8adcda9c38a41edc2dce357dce0 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 13:57:23 -0600
Subject: [PATCH 20/46] Incorporate scheduler

---
 juno_build/src/lib.rs         |  46 +--------
 juno_frontend/src/codegen.rs  |   2 +-
 juno_frontend/src/lib.rs      | 105 ++-------------------
 juno_frontend/src/main.rs     |  35 +------
 juno_scheduler/src/default.rs |  62 +++++++++++++
 juno_scheduler/src/labels.rs  |  11 ++-
 juno_scheduler/src/lib.rs     | 169 +++++++++++++++++-----------------
 juno_scheduler/src/pm.rs      |   9 +-
 8 files changed, 177 insertions(+), 262 deletions(-)
 create mode 100644 juno_scheduler/src/default.rs

diff --git a/juno_build/src/lib.rs b/juno_build/src/lib.rs
index 62fe8ec3..4532db18 100644
--- a/juno_build/src/lib.rs
+++ b/juno_build/src/lib.rs
@@ -6,7 +6,6 @@ use std::env::{current_dir, var};
 use std::fs::{create_dir_all, read_to_string};
 use std::path::{Path, PathBuf};
 
-
 // JunoCompiler is used to compile juno files into a library and manifest file appropriately to
 // import the definitions into a rust project via the juno! macro defined below
 // You can also specify a Hercules IR file instead of a Juno file and this will compile that IR
@@ -15,9 +14,7 @@ pub struct JunoCompiler {
     ir_src_path: Option<PathBuf>,
     src_path: Option<PathBuf>,
     out_path: Option<PathBuf>,
-    verify: JunoVerify,
-    x_dot: bool,
-    schedule: JunoSchedule,
+    schedule: Option<String>,
 }
 
 impl JunoCompiler {
@@ -26,9 +23,7 @@ impl JunoCompiler {
             ir_src_path: None,
             src_path: None,
             out_path: None,
-            verify: JunoVerify::None,
-            x_dot: false,
-            schedule: JunoSchedule::None,
+            schedule: None,
         }
     }
 
@@ -119,35 +114,6 @@ impl JunoCompiler {
         );
     }
 
-    pub fn verify(mut self, enabled: bool) -> Self {
-        if enabled && !self.verify.verify() {
-            self.verify = JunoVerify::JunoOpts;
-        } else if !enabled && self.verify.verify() {
-            self.verify = JunoVerify::None;
-        }
-        self
-    }
-
-    pub fn verify_all(mut self, enabled: bool) -> Self {
-        if enabled {
-            self.verify = JunoVerify::AllPasses;
-        } else if !enabled && self.verify.verify_all() {
-            self.verify = JunoVerify::JunoOpts;
-        }
-        self
-    }
-
-    pub fn x_dot(mut self, enabled: bool) -> Self {
-        self.x_dot = enabled;
-        self
-    }
-
-    // Sets the schedule to be the default schedule
-    pub fn default_schedule(mut self) -> Self {
-        self.schedule = JunoSchedule::DefaultSchedule;
-        self
-    }
-
     // Set the schedule as a schedule file in the src directory
     pub fn schedule_in_src<P>(mut self, file: P) -> Result<Self, String>
     where
@@ -166,7 +132,7 @@ impl JunoCompiler {
         };
         path.push("src");
         path.push(file.as_ref());
-        self.schedule = JunoSchedule::Schedule(path.to_str().unwrap().to_string());
+        self.schedule = Some(path.to_str().unwrap().to_string());
 
         // Tell cargo to rerun if the schedule changes
         println!(
@@ -183,8 +149,6 @@ impl JunoCompiler {
             ir_src_path,
             src_path,
             out_path: Some(out_path),
-            verify,
-            x_dot,
             schedule,
         } = self
         else {
@@ -196,7 +160,7 @@ impl JunoCompiler {
         if let Some(src_path) = src_path {
             let src_file = src_path.to_str().unwrap().to_string();
 
-            match compile(src_file, verify, x_dot, schedule, out_dir) {
+            match compile(src_file, schedule, out_dir) {
                 Ok(()) => Ok(()),
                 Err(errs) => Err(format!("{}", errs)),
             }
@@ -216,7 +180,7 @@ impl JunoCompiler {
                 return Err("Unable to parse Hercules IR file.".to_string());
             };
 
-            match compile_ir(ir_mod, verify, x_dot, schedule, out_dir, module_name) {
+            match compile_ir(ir_mod, schedule, out_dir, module_name) {
                 Ok(()) => Ok(()),
                 Err(errs) => Err(format!("{}", errs)),
             }
diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 5cfef27d..0a52a88e 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -86,7 +86,7 @@ impl CodeGenerator<'_> {
             uid: _,
             functions,
             worklist: _,
-            juno_info
+            juno_info,
         } = self;
 
         (builder.finish(), juno_info)
diff --git a/juno_frontend/src/lib.rs b/juno_frontend/src/lib.rs
index 6e18f5e8..0256683d 100644
--- a/juno_frontend/src/lib.rs
+++ b/juno_frontend/src/lib.rs
@@ -11,10 +11,10 @@ mod types;
 extern crate hercules_ir;
 extern crate juno_scheduler;
 
+use juno_scheduler::{schedule_hercules, schedule_juno};
 use std::fmt;
 use std::path::Path;
 
-
 pub enum JunoVerify {
     None,
     JunoOpts,
@@ -81,9 +81,7 @@ impl fmt::Display for ErrorMessage {
 
 pub fn compile(
     src_file: String,
-    verify: JunoVerify,
-    x_dot: bool,
-    schedule: JunoSchedule,
+    schedule: Option<String>,
     output_dir: String,
 ) -> Result<(), ErrorMessage> {
     let src_file_path = Path::new(&src_file);
@@ -97,105 +95,16 @@ pub fn compile(
     };
     let (module, juno_info) = codegen::codegen_program(prg);
 
-    compile_ir(module, verify, x_dot, schedule, output_dir, module_name)
+    schedule_juno(module, juno_info, schedule, output_dir, module_name)
+        .map_err(|s| ErrorMessage::SchedulingError(s))
 }
 
 pub fn compile_ir(
     module: hercules_ir::ir::Module,
-    verify: JunoVerify,
-    x_dot: bool,
-    schedule: JunoSchedule,
+    schedule: Option<String>,
     output_dir: String,
     module_name: String,
 ) -> Result<(), ErrorMessage> {
-    let mut pm = match schedule {
-        JunoSchedule::None => hercules_opt::pass::PassManager::new(module),
-        _ => todo!(),
-        /*
-        JunoSchedule::DefaultSchedule => {
-            let mut pm = hercules_opt::pass::PassManager::new(module);
-            pm.make_plans();
-            pm
-        }
-        JunoSchedule::Schedule(file) => {
-            let Some(func_info) = func_info else {
-                return Err(ErrorMessage::SchedulingError(
-                    "Cannot schedule, no function information provided".to_string(),
-                ));
-            };
-
-            match juno_scheduler::schedule(&module, func_info, file) {
-                Ok(plans) => {
-                    let mut pm = hercules_opt::pass::PassManager::new(module);
-                    pm.set_plans(plans);
-                    pm
-                }
-                Err(msg) => {
-                    return Err(ErrorMessage::SchedulingError(msg));
-                }
-            }
-        }
-        */
-    };
-    if verify.verify() || verify.verify_all() {
-        pm.add_pass(hercules_opt::pass::Pass::Verify);
-    }
-    add_verified_pass!(pm, verify, GVN);
-    add_verified_pass!(pm, verify, PhiElim);
-    add_pass!(pm, verify, DCE);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-    add_pass!(pm, verify, Inline);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-    // Inlining may make some functions uncalled, so run this pass.
-    // In general, this should always be run after inlining.
-    add_pass!(pm, verify, DeleteUncalled);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-    // Run SROA pretty early (though after inlining which can make SROA more effective) so that
-    // CCP, GVN, etc. can work on the result of SROA
-    add_pass!(pm, verify, InterproceduralSROA);
-    add_pass!(pm, verify, SROA);
-    // We run phi-elim again because SROA can introduce new phis that might be able to be
-    // simplified
-    add_verified_pass!(pm, verify, PhiElim);
-    add_pass!(pm, verify, DCE);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-    add_pass!(pm, verify, CCP);
-    add_pass!(pm, verify, DCE);
-    add_pass!(pm, verify, GVN);
-    add_pass!(pm, verify, DCE);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-    //add_pass!(pm, verify, Forkify);
-    //add_pass!(pm, verify, ForkGuardElim);
-    add_verified_pass!(pm, verify, DCE);
-    add_pass!(pm, verify, ForkSplit);
-    add_pass!(pm, verify, Unforkify);
-    add_pass!(pm, verify, GVN);
-    add_verified_pass!(pm, verify, DCE);
-    add_pass!(pm, verify, LegalizeReferenceSemantics);
-    add_pass!(pm, verify, Outline);
-    add_pass!(pm, verify, InterproceduralSROA);
-    add_pass!(pm, verify, SROA);
-    add_pass!(pm, verify, InferSchedules);
-    add_verified_pass!(pm, verify, DCE);
-    if x_dot {
-        pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
-    }
-
-    add_pass!(pm, verify, LegalizeReferenceSemantics);
-    add_verified_pass!(pm, verify, DCE);
-    add_pass!(pm, verify, LegalizeReferenceSemantics);
-    pm.add_pass(hercules_opt::pass::Pass::Codegen(output_dir, module_name));
-    pm.run_passes();
-
-    Ok(())
+    schedule_hercules(module, schedule, output_dir, module_name)
+        .map_err(|s| ErrorMessage::SchedulingError(s))
 }
diff --git a/juno_frontend/src/main.rs b/juno_frontend/src/main.rs
index 4624e716..43986fa1 100644
--- a/juno_frontend/src/main.rs
+++ b/juno_frontend/src/main.rs
@@ -2,53 +2,22 @@ extern crate clap;
 
 use juno_compiler::*;
 
-use clap::{ArgGroup, Parser};
+use clap::Parser;
 
 use std::path::PathBuf;
 
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
-#[clap(group(
-        ArgGroup::new("scheduling")
-            .required(false)
-            .args(&["schedule", "default_schedule", "no_schedule"])))]
 struct Cli {
     src_file: String,
-    #[clap(short, long)]
-    verify: bool,
-    #[clap(long = "verify-all")]
-    verify_all: bool,
-    #[arg(short, long = "x-dot")]
-    x_dot: bool,
     #[clap(short, long, value_name = "SCHEDULE")]
     schedule: Option<String>,
-    #[clap(short, long = "default-schedule")]
-    default_schedule: bool,
-    #[clap(short, long)]
-    no_schedule: bool,
     #[arg(short, long = "output-dir", value_name = "OUTPUT DIR")]
     output_dir: Option<String>,
 }
 
 fn main() {
     let args = Cli::parse();
-    let verify = if args.verify_all {
-        JunoVerify::AllPasses
-    } else if args.verify {
-        JunoVerify::JunoOpts
-    } else {
-        JunoVerify::None
-    };
-    let schedule = match args.schedule {
-        Some(file) => JunoSchedule::Schedule(file),
-        None => {
-            if args.default_schedule {
-                JunoSchedule::DefaultSchedule
-            } else {
-                JunoSchedule::None
-            }
-        }
-    };
     let output_dir = match args.output_dir {
         Some(dir) => dir,
         None => PathBuf::from(args.src_file.clone())
@@ -58,7 +27,7 @@ fn main() {
             .unwrap()
             .to_string(),
     };
-    match compile(args.src_file, verify, args.x_dot, schedule, output_dir) {
+    match compile(args.src_file, args.schedule, output_dir) {
         Ok(()) => {}
         Err(errs) => {
             eprintln!("{}", errs);
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
new file mode 100644
index 00000000..f1c13516
--- /dev/null
+++ b/juno_scheduler/src/default.rs
@@ -0,0 +1,62 @@
+use crate::ir::*;
+
+macro_rules! pass {
+    ($p:ident) => {
+        ScheduleStmt::Let {
+            var: String::from("_"),
+            exp: ScheduleExp::RunPass {
+                pass: Pass::$p,
+                args: vec![],
+                on: Selector::Everything(),
+            },
+        }
+    };
+}
+
+macro_rules! default_schedule {
+    () => {
+        ScheduleStmt::Block {
+            body: vec![],
+        }
+    };
+    ($($p:ident),+ $(,)?) => {
+        ScheduleStmt::Block {
+            body: vec![$(pass!($p)),+],
+        }
+    };
+}
+
+// Defualt schedule, which is used if no schedule is provided
+pub fn default_schedule() -> ScheduleStmt {
+    default_schedule![
+        GVN,
+        PhiElim,
+        DCE,
+        Inline,
+        DeleteUncalled,
+        InterproceduralSROA,
+        SROA,
+        PhiElim,
+        DCE,
+        CCP,
+        DCE,
+        GVN,
+        DCE,
+        /*Forkify,*/
+        /*ForkGuardElim,*/
+        DCE,
+        ForkSplit,
+        Unforkify,
+        GVN,
+        DCE,
+        LegalizeReferenceSemantics,
+        Outline,
+        InterproceduralSROA,
+        SROA,
+        InferSchedules,
+        DCE,
+        LegalizeReferenceSemantics,
+        DCE,
+        LegalizeReferenceSemantics
+    ]
+}
diff --git a/juno_scheduler/src/labels.rs b/juno_scheduler/src/labels.rs
index 178983b1..6690e17a 100644
--- a/juno_scheduler/src/labels.rs
+++ b/juno_scheduler/src/labels.rs
@@ -35,8 +35,10 @@ pub struct JunoFunctions {
 }
 
 impl JunoInfo {
-    pub fn new<I>(funcs: I) -> Self 
-    where I: Iterator<Item = String> {
+    pub fn new<I>(funcs: I) -> Self
+    where
+        I: Iterator<Item = String>,
+    {
         let mut func_names = HashMap::new();
         let mut func_ids = vec![];
 
@@ -45,7 +47,10 @@ impl JunoInfo {
             func_ids.push(vec![]);
         }
 
-        JunoInfo { func_names, func_info: JunoFunctions { func_ids } }
+        JunoInfo {
+            func_names,
+            func_info: JunoFunctions { func_ids },
+        }
     }
 }
 
diff --git a/juno_scheduler/src/lib.rs b/juno_scheduler/src/lib.rs
index 719497cd..8d92ea09 100644
--- a/juno_scheduler/src/lib.rs
+++ b/juno_scheduler/src/lib.rs
@@ -16,20 +16,19 @@ mod parser;
 use crate::parser::lexer;
 
 mod compile;
+mod default;
 mod ir;
 pub mod labels;
 mod pm;
 
 use crate::compile::*;
+use crate::default::*;
 use crate::ir::*;
 use crate::labels::*;
 use crate::pm::*;
 
-pub fn schedule_juno(
-    module: Module,
-    juno_info: JunoInfo,
-    sched_filename: String,
-) -> Result<Module, String> {
+// Given a schedule's filename parse and process the schedule
+fn build_schedule(sched_filename: String) -> Result<ScheduleStmt, String> {
     if let Ok(mut file) = File::open(sched_filename) {
         let mut contents = String::new();
         if let Ok(_) = file.read_to_string(&mut contents) {
@@ -41,26 +40,7 @@ pub fn schedule_juno(
                 match res {
                     None => Err(format!("No parse errors, but parsing the schedule failed")),
                     Some(schd) => {
-                        let schd = compile_schedule(schd, &lexer)
-                            .map_err(|e| format!("Schedule Error: {}", e))?;
-
-                        let mut strings = StringTable::new();
-                        let mut env = Env::new();
-
-                        env.open_scope();
-
-                        let JunoInfo {
-                            func_names,
-                            func_info,
-                        } = juno_info;
-                        for (func_name, func_id) in func_names {
-                            let func_name = strings.lookup_string(func_name);
-                            env.insert(func_name, Value::JunoFunction { func: func_id });
-                        }
-
-                        env.open_scope();
-                        schedule(module, schd, strings, env, func_info)
-                            .map_err(|e| format!("Scheduling Error: {}", e))
+                        compile_schedule(schd, &lexer).map_err(|e| format!("Schedule Error: {}", e))
                     }
                 }
             } else {
@@ -83,63 +63,88 @@ pub fn schedule_juno(
     }
 }
 
-pub fn schedule_hercules(module: Module, sched_filename: String) -> Result<Module, String> {
-    if let Ok(mut file) = File::open(sched_filename) {
-        let mut contents = String::new();
-        if let Ok(_) = file.read_to_string(&mut contents) {
-            let lexerdef = lexer::lexerdef();
-            let lexer = lexerdef.lexer(&contents);
-            let (res, errs) = parser::parse(&lexer);
-
-            if errs.is_empty() {
-                match res {
-                    None => Err(format!("No parse errors, but parsing the schedule failed")),
-                    Some(schd) => {
-                        let schd = compile_schedule(schd, &lexer)
-                            .map_err(|e| format!("Schedule Error: {}", e))?;
-
-                        let mut strings = StringTable::new();
-                        let mut env = Env::new();
-
-                        env.open_scope();
-
-                        for (idx, func) in module.functions.iter().enumerate() {
-                            let func_name = strings.lookup_string(func.name.clone());
-                            env.insert(
-                                func_name,
-                                Value::HerculesFunction {
-                                    func: FunctionID::new(idx),
-                                },
-                            );
-                        }
-
-                        env.open_scope();
-                        schedule(
-                            module,
-                            schd,
-                            strings,
-                            env,
-                            JunoFunctions { func_ids: vec![] },
-                        )
-                        .map_err(|e| format!("Scheduling Error: {}", e))
-                    }
-                }
-            } else {
-                Err(errs
-                    .iter()
-                    .map(|e| {
-                        format!(
-                            "Schedule Syntax Error: {}",
-                            e.pp(&lexer, &parser::token_epp)
-                        )
-                    })
-                    .collect::<Vec<_>>()
-                    .join("\n"))
-            }
-        } else {
-            Err(format!("Unable to read schedule"))
-        }
+fn process_schedule(sched_filename: Option<String>) -> Result<ScheduleStmt, String> {
+    if let Some(name) = sched_filename {
+        build_schedule(name)
     } else {
-        Err(format!("Unable to open schedule"))
+        Ok(default_schedule())
+    }
+}
+
+pub fn schedule_juno(
+    module: Module,
+    juno_info: JunoInfo,
+    sched_filename: Option<String>,
+    output_dir: String,
+    module_name: String,
+) -> Result<(), String> {
+    let sched = process_schedule(sched_filename)?;
+
+    // Prepare the scheduler's string table and environment
+    // For this, we need to put all of the Juno functions into the environment
+    // and string table
+    let mut strings = StringTable::new();
+    let mut env = Env::new();
+
+    env.open_scope();
+
+    let JunoInfo {
+        func_names,
+        func_info,
+    } = juno_info;
+    for (func_name, func_id) in func_names {
+        let func_name = strings.lookup_string(func_name);
+        env.insert(func_name, Value::JunoFunction { func: func_id });
     }
+
+    env.open_scope();
+    schedule_codegen(
+        module,
+        sched,
+        strings,
+        env,
+        func_info,
+        output_dir,
+        module_name,
+    )
+    .map_err(|e| format!("Scheduling Error: {}", e))
+}
+
+pub fn schedule_hercules(
+    module: Module,
+    sched_filename: Option<String>,
+    output_dir: String,
+    module_name: String,
+) -> Result<(), String> {
+    let sched = process_schedule(sched_filename)?;
+
+    // Prepare the scheduler's string table and environment
+    // For this, we put all of the Hercules function names into the environment
+    // and string table
+    let mut strings = StringTable::new();
+    let mut env = Env::new();
+
+    env.open_scope();
+
+    for (idx, func) in module.functions.iter().enumerate() {
+        let func_name = strings.lookup_string(func.name.clone());
+        env.insert(
+            func_name,
+            Value::HerculesFunction {
+                func: FunctionID::new(idx),
+            },
+        );
+    }
+
+    env.open_scope();
+    schedule_codegen(
+        module,
+        sched,
+        strings,
+        env,
+        JunoFunctions { func_ids: vec![] },
+        output_dir,
+        module_name,
+    )
+    .map_err(|e| format!("Scheduling Error: {}", e))
 }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 89f9fb3f..75cfe781 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -141,21 +141,22 @@ impl fmt::Display for SchedulerError {
     }
 }
 
-pub fn schedule(
+pub fn schedule_codegen(
     mut module: Module,
     schedule: ScheduleStmt,
     mut stringtab: StringTable,
     mut env: Env<usize, Value>,
     mut functions: JunoFunctions,
-) -> Result<Module, SchedulerError> {
+    output_dir: String,
+    module_name: String,
+) -> Result<(), SchedulerError> {
     schedule_interpret(
         &mut module,
         &schedule,
         &mut stringtab,
         &mut env,
         &mut functions,
-    )?;
-    Ok(module)
+    )
 }
 
 fn schedule_interpret(
-- 
GitLab


From 3fbbee1097ffeca8506b77ce2e1ccef72831c821 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 14:08:23 -0600
Subject: [PATCH 21/46] Add booleans to scheduler

---
 juno_scheduler/src/compile.rs | 3 +++
 juno_scheduler/src/lang.l     | 3 +++
 juno_scheduler/src/lang.y     | 5 +++++
 3 files changed, 11 insertions(+)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index f56dbaff..7c44154f 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -352,6 +352,9 @@ fn compile_expr(
             let val: usize = lexer.span_str(span).parse().expect("Parsing");
             Ok(ExprResult::Expr(ir::ScheduleExp::Integer { val }))
         }
+        parser::Expr::Boolean { span: _, val } => {
+            Ok(ExprResult::Expr(ir::ScheduleExp::Boolean { val }))
+        }
         parser::Expr::Field {
             span: _,
             lhs,
diff --git a/juno_scheduler/src/lang.l b/juno_scheduler/src/lang.l
index a940a267..8c2c1847 100644
--- a/juno_scheduler/src/lang.l
+++ b/juno_scheduler/src/lang.l
@@ -27,6 +27,9 @@ on              "on"
 set             "set"
 target          "target"
 
+true            "true"
+false           "false"
+
 \(              "("
 \)              ")"
 \<              "<"
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index 3f45c8aa..597747c4 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -43,6 +43,10 @@ Expr -> Expr
       { Expr::Variable { span: span_of_tok($1) } }
   | 'INT'
       { Expr::Integer { span: span_of_tok($1) } }
+  | 'true'
+      { Expr::Boolean { span: $span, val: true } }
+  | 'false'
+      { Expr::Boolean { span: $span, val: false } }
   | Expr '.' 'ID'
       { Expr::Field { span: $span, lhs: Box::new($1), field: span_of_tok($3) } }
   | Expr '@' 'ID'
@@ -145,6 +149,7 @@ pub enum Expr {
   Macro       { span: Span, name: Span, args: Vec<Expr>, selection: Selector },
   Variable    { span: Span },
   Integer     { span: Span },
+  Boolean     { span: Span, val: bool },
   Field       { span: Span, lhs: Box<Expr>, field: Span },
   BlockExpr   { span: Span, body: Box<OperationList> },
   Record      { span: Span, fields: Vec<(Span, Expr)> },
-- 
GitLab


From 5364466dfd833172479be62f1e656dcca25c310f Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 15:01:18 -0600
Subject: [PATCH 22/46] Start implementation of pass manager

---
 Cargo.lock                |   2 +
 juno_scheduler/Cargo.toml |   2 +
 juno_scheduler/src/pm.rs  | 530 ++++++++++++++++++++++++++++++++------
 3 files changed, 461 insertions(+), 73 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 5210d473..2258a986 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -775,7 +775,9 @@ name = "juno_scheduler"
 version = "0.0.1"
 dependencies = [
  "cfgrammar",
+ "hercules_cg",
  "hercules_ir",
+ "hercules_opt",
  "juno_utils",
  "lrlex",
  "lrpar",
diff --git a/juno_scheduler/Cargo.toml b/juno_scheduler/Cargo.toml
index c41b9310..15b1db32 100644
--- a/juno_scheduler/Cargo.toml
+++ b/juno_scheduler/Cargo.toml
@@ -13,5 +13,7 @@ lrpar = "0.13"
 cfgrammar = "0.13"
 lrlex = "0.13"
 lrpar = "0.13"
+hercules_cg = { path = "../hercules_cg" }
 hercules_ir = { path = "../hercules_ir" }
+hercules_opt = { path = "../hercules_opt" }
 juno_utils = { path = "../juno_utils" }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 75cfe781..71265461 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,3 +1,8 @@
+extern crate hercules_cg;
+extern crate hercules_opt;
+
+use self::hercules_cg::*;
+use self::hercules_opt::*;
 use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
@@ -5,8 +10,13 @@ use hercules_ir::*;
 use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
 
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
+use std::env::temp_dir;
 use std::fmt;
+use std::fs::File;
+use std::io::Write;
+use std::iter::zip;
+use std::process::{Command, Stdio};
 
 #[derive(Debug, Clone)]
 pub enum Value {
@@ -128,6 +138,8 @@ pub enum SchedulerError {
     UndefinedField(String),
     UndefinedLabel(String),
     SemanticError(String),
+    PassError { pass: String, error: String },
+    FixpointFailure(),
 }
 
 impl fmt::Display for SchedulerError {
@@ -137,8 +149,334 @@ impl fmt::Display for SchedulerError {
             SchedulerError::UndefinedField(nm) => write!(f, "No field '{}'", nm),
             SchedulerError::UndefinedLabel(nm) => write!(f, "No label '{}'", nm),
             SchedulerError::SemanticError(msg) => write!(f, "{}", msg),
+            SchedulerError::PassError { pass, error } => {
+                write!(f, "Error in pass {}: {}", pass, error)
+            }
+            SchedulerError::FixpointFailure() => {
+                write!(f, "Fixpoint did not converge within limit")
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+struct PassManager {
+    module: Module,
+
+    // Cached analysis results.
+    pub def_uses: Option<Vec<ImmutableDefUseMap>>,
+    pub reverse_postorders: Option<Vec<Vec<NodeID>>>,
+    pub typing: Option<ModuleTyping>,
+    pub control_subgraphs: Option<Vec<Subgraph>>,
+    pub doms: Option<Vec<DomTree>>,
+    pub postdoms: Option<Vec<DomTree>>,
+    pub fork_join_maps: Option<Vec<HashMap<NodeID, NodeID>>>,
+    pub fork_join_nests: Option<Vec<HashMap<NodeID, Vec<NodeID>>>>,
+    pub loops: Option<Vec<LoopTree>>,
+    pub reduce_cycles: Option<Vec<HashMap<NodeID, HashSet<NodeID>>>>,
+    pub data_nodes_in_fork_joins: Option<Vec<HashMap<NodeID, HashSet<NodeID>>>>,
+    pub bbs: Option<Vec<BasicBlocks>>,
+    pub collection_objects: Option<CollectionObjects>,
+    pub callgraph: Option<CallGraph>,
+}
+
+impl PassManager {
+    fn new(module: Module) -> Self {
+        PassManager {
+            module,
+            def_uses: None,
+            reverse_postorders: None,
+            typing: None,
+            control_subgraphs: None,
+            doms: None,
+            postdoms: None,
+            fork_join_maps: None,
+            fork_join_nests: None,
+            loops: None,
+            reduce_cycles: None,
+            data_nodes_in_fork_joins: None,
+            bbs: None,
+            collection_objects: None,
+            callgraph: None,
+        }
+    }
+
+    pub fn make_def_uses(&mut self) {
+        if self.def_uses.is_none() {
+            self.def_uses = Some(self.module.functions.iter().map(def_use).collect());
+        }
+    }
+
+    pub fn make_reverse_postorders(&mut self) {
+        if self.reverse_postorders.is_none() {
+            self.make_def_uses();
+            self.reverse_postorders = Some(
+                self.def_uses
+                    .as_ref()
+                    .unwrap()
+                    .iter()
+                    .map(reverse_postorder)
+                    .collect(),
+            );
+        }
+    }
+
+    pub fn make_typing(&mut self) {
+        if self.typing.is_none() {
+            self.make_reverse_postorders();
+            self.typing = Some(
+                typecheck(&mut self.module, self.reverse_postorders.as_ref().unwrap()).unwrap(),
+            );
+        }
+    }
+
+    pub fn make_control_subgraphs(&mut self) {
+        if self.control_subgraphs.is_none() {
+            self.make_def_uses();
+            self.control_subgraphs = Some(
+                zip(&self.module.functions, self.def_uses.as_ref().unwrap())
+                    .map(|(function, def_use)| control_subgraph(function, def_use))
+                    .collect(),
+            );
+        }
+    }
+
+    pub fn make_doms(&mut self) {
+        if self.doms.is_none() {
+            self.make_control_subgraphs();
+            self.doms = Some(
+                self.control_subgraphs
+                    .as_ref()
+                    .unwrap()
+                    .iter()
+                    .map(|subgraph| dominator(subgraph, NodeID::new(0)))
+                    .collect(),
+            );
+        }
+    }
+
+    pub fn make_postdoms(&mut self) {
+        if self.postdoms.is_none() {
+            self.make_control_subgraphs();
+            self.postdoms = Some(
+                zip(
+                    self.control_subgraphs.as_ref().unwrap().iter(),
+                    self.module.functions.iter(),
+                )
+                .map(|(subgraph, function)| dominator(subgraph, NodeID::new(function.nodes.len())))
+                .collect(),
+            );
+        }
+    }
+
+    pub fn make_fork_join_maps(&mut self) {
+        if self.fork_join_maps.is_none() {
+            self.make_control_subgraphs();
+            self.fork_join_maps = Some(
+                zip(
+                    self.module.functions.iter(),
+                    self.control_subgraphs.as_ref().unwrap().iter(),
+                )
+                .map(|(function, subgraph)| fork_join_map(function, subgraph))
+                .collect(),
+            );
+        }
+    }
+
+    pub fn make_fork_join_nests(&mut self) {
+        if self.fork_join_nests.is_none() {
+            self.make_doms();
+            self.make_fork_join_maps();
+            self.fork_join_nests = Some(
+                zip(
+                    self.module.functions.iter(),
+                    zip(
+                        self.doms.as_ref().unwrap().iter(),
+                        self.fork_join_maps.as_ref().unwrap().iter(),
+                    ),
+                )
+                .map(|(function, (dom, fork_join_map))| {
+                    compute_fork_join_nesting(function, dom, fork_join_map)
+                })
+                .collect(),
+            );
+        }
+    }
+
+    pub fn make_loops(&mut self) {
+        if self.loops.is_none() {
+            self.make_control_subgraphs();
+            self.make_doms();
+            self.make_fork_join_maps();
+            let control_subgraphs = self.control_subgraphs.as_ref().unwrap().iter();
+            let doms = self.doms.as_ref().unwrap().iter();
+            let fork_join_maps = self.fork_join_maps.as_ref().unwrap().iter();
+            self.loops = Some(
+                zip(control_subgraphs, zip(doms, fork_join_maps))
+                    .map(|(control_subgraph, (dom, fork_join_map))| {
+                        loops(control_subgraph, NodeID::new(0), dom, fork_join_map)
+                    })
+                    .collect(),
+            );
+        }
+    }
+
+    pub fn make_reduce_cycles(&mut self) {
+        if self.reduce_cycles.is_none() {
+            self.make_def_uses();
+            let def_uses = self.def_uses.as_ref().unwrap().iter();
+            self.reduce_cycles = Some(
+                zip(self.module.functions.iter(), def_uses)
+                    .map(|(function, def_use)| reduce_cycles(function, def_use))
+                    .collect(),
+            );
+        }
+    }
+
+    pub fn make_data_nodes_in_fork_joins(&mut self) {
+        if self.data_nodes_in_fork_joins.is_none() {
+            self.make_def_uses();
+            self.make_fork_join_maps();
+            self.data_nodes_in_fork_joins = Some(
+                zip(
+                    self.module.functions.iter(),
+                    zip(
+                        self.def_uses.as_ref().unwrap().iter(),
+                        self.fork_join_maps.as_ref().unwrap().iter(),
+                    ),
+                )
+                .map(|(function, (def_use, fork_join_map))| {
+                    data_nodes_in_fork_joins(function, def_use, fork_join_map)
+                })
+                .collect(),
+            );
         }
     }
+
+    pub fn make_collection_objects(&mut self) {
+        if self.collection_objects.is_none() {
+            self.make_reverse_postorders();
+            self.make_typing();
+            self.make_callgraph();
+            let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
+            let typing = self.typing.as_ref().unwrap();
+            let callgraph = self.callgraph.as_ref().unwrap();
+            self.collection_objects = Some(collection_objects(
+                &self.module,
+                reverse_postorders,
+                typing,
+                callgraph,
+            ));
+        }
+    }
+
+    pub fn make_callgraph(&mut self) {
+        if self.callgraph.is_none() {
+            self.callgraph = Some(callgraph(&self.module));
+        }
+    }
+
+    fn codegen(mut self, output_dir: String, module_name: String) -> Result<(), SchedulerError> {
+        self.make_reverse_postorders();
+        self.make_typing();
+        self.make_control_subgraphs();
+        self.make_collection_objects();
+        self.make_callgraph();
+
+        let PassManager {
+            module,
+            reverse_postorders: Some(reverse_postorders),
+            typing: Some(typing),
+            control_subgraphs: Some(control_subgraphs),
+            bbs: Some(bbs),
+            collection_objects: Some(collection_objects),
+            callgraph: Some(callgraph),
+            ..
+        } = self
+        else {
+            return Err(SchedulerError::PassError {
+                pass: "codegen".to_string(),
+                error: "Missing basic blocks".to_string(),
+            });
+        };
+
+        let devices = device_placement(&module.functions, &callgraph);
+
+        let mut rust_rt = String::new();
+        let mut llvm_ir = String::new();
+        for idx in 0..module.functions.len() {
+            match devices[idx] {
+                Device::LLVM => cpu_codegen(
+                    &module.functions[idx],
+                    &module.types,
+                    &module.constants,
+                    &module.dynamic_constants,
+                    &reverse_postorders[idx],
+                    &typing[idx],
+                    &control_subgraphs[idx],
+                    &bbs[idx],
+                    &mut llvm_ir,
+                )
+                .map_err(|e| SchedulerError::PassError {
+                    pass: "cpu codegen".to_string(),
+                    error: format!("{}", e),
+                })?,
+                Device::AsyncRust => rt_codegen(
+                    FunctionID::new(idx),
+                    &module,
+                    &reverse_postorders[idx],
+                    &typing[idx],
+                    &control_subgraphs[idx],
+                    &bbs[idx],
+                    &collection_objects,
+                    &callgraph,
+                    &devices,
+                    &mut rust_rt,
+                )
+                .map_err(|e| SchedulerError::PassError {
+                    pass: "rust codegen".to_string(),
+                    error: format!("{}", e),
+                })?,
+                _ => todo!(),
+            }
+        }
+        println!("{}", llvm_ir);
+        println!("{}", rust_rt);
+
+        // Write the LLVM IR into a temporary file.
+        let mut tmp_path = temp_dir();
+        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(&output_archive)
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .spawn()
+            .expect("Error running clang. Is it installed?");
+        assert!(clang_process.wait().unwrap().success());
+        println!("{}", output_archive);
+
+        // Write the Rust runtime into a file.
+        let output_rt = format!("{}/rt_{}.hrt", output_dir, module_name);
+        let mut file =
+            File::create(&output_rt).expect("PANIC: Unable to open output Rust runtime file.");
+        file.write_all(rust_rt.as_bytes())
+            .expect("PANIC: Unable to write output Rust runtime file contents.");
+        println!("{}", output_rt);
+
+        Ok(())
+    }
 }
 
 pub fn schedule_codegen(
@@ -146,42 +484,73 @@ pub fn schedule_codegen(
     schedule: ScheduleStmt,
     mut stringtab: StringTable,
     mut env: Env<usize, Value>,
-    mut functions: JunoFunctions,
+    functions: JunoFunctions,
     output_dir: String,
     module_name: String,
 ) -> Result<(), SchedulerError> {
-    schedule_interpret(
-        &mut module,
-        &schedule,
-        &mut stringtab,
-        &mut env,
-        &mut functions,
-    )
+    let mut pm = PassManager::new(module);
+    let _ = schedule_interpret(&mut pm, &schedule, &mut stringtab, &mut env, &functions)?;
+    pm.codegen(output_dir, module_name)
 }
 
+// Interpreter for statements and expressions returns a bool indicating whether
+// any optimization ran and changed the IR. This is used for implementing
+// the fixpoint
 fn schedule_interpret(
-    module: &mut Module,
+    pm: &mut PassManager,
     schedule: &ScheduleStmt,
     stringtab: &mut StringTable,
     env: &mut Env<usize, Value>,
-    functions: &mut JunoFunctions,
-) -> Result<(), SchedulerError> {
+    functions: &JunoFunctions,
+) -> Result<bool, SchedulerError> {
     match schedule {
-        ScheduleStmt::Fixpoint { .. } => todo!("fixpoint not implemented"),
+        ScheduleStmt::Fixpoint { body, limit } => {
+            let mut i = 0;
+            loop {
+                // If no change was made, we've reached the fixpoint and are done
+                if !schedule_interpret(pm, body, stringtab, env, functions)? {
+                    break;
+                }
+                // Otherwise, increase the iteration count and check the limit
+                i += 1;
+                match limit {
+                    FixpointLimit::NoLimit() => {}
+                    FixpointLimit::PrintIter() => {
+                        println!("Finished Iteration {}", i - 1)
+                    }
+                    FixpointLimit::StopAfter(n) => {
+                        if i >= *n {
+                            break;
+                        }
+                    }
+                    FixpointLimit::PanicAfter(n) => {
+                        if i >= *n {
+                            return Err(SchedulerError::FixpointFailure());
+                        }
+                    }
+                }
+            }
+            // If we ran just 1 iteration, then no changes were made and otherwise some changes
+            // were made
+            Ok(i > 1)
+        }
         ScheduleStmt::Block { body } => {
+            let mut modified = false;
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, stringtab, env, functions)?;
+                modified |= schedule_interpret(pm, command, stringtab, env, functions)?;
             }
             env.close_scope();
+            Ok(modified)
         }
         ScheduleStmt::Let { var, exp } => {
-            let res = interp_expr(module, exp, stringtab, env, functions)?;
+            let (res, modified) = interp_expr(pm, exp, stringtab, env, functions)?;
             let var_id = stringtab.lookup_string(var.clone());
             env.insert(var_id, res);
+            Ok(modified)
         }
         ScheduleStmt::Assign { var, exp } => {
-            let res = interp_expr(module, exp, stringtab, env, functions)?;
+            let (res, modified) = interp_expr(pm, exp, stringtab, env, functions)?;
             let var_id = stringtab.lookup_string(var.clone());
             match env.lookup_mut(&var_id) {
                 None => {
@@ -191,129 +560,144 @@ fn schedule_interpret(
                     *val = res;
                 }
             }
+            Ok(modified)
         }
         ScheduleStmt::AddSchedule { sched, on } => match on {
-            Selector::Everything() => {
-                return Err(SchedulerError::SemanticError(
-                    "Cannot apply schedule to everything".to_string(),
-                ));
-            }
+            Selector::Everything() => Err(SchedulerError::SemanticError(
+                "Cannot apply schedule to everything".to_string(),
+            )),
             Selector::Selection(selection) => {
+                let mut changed = false;
                 for label in selection {
-                    let label = interp_expr(module, label, stringtab, env, functions)?;
-                    add_schedule(module, sched.clone(), label.as_labels()?);
+                    let (label, modified) = interp_expr(pm, label, stringtab, env, functions)?;
+                    changed |= modified;
+                    add_schedule(pm, sched.clone(), label.as_labels()?);
                 }
+                Ok(changed)
             }
         },
         ScheduleStmt::AddDevice { device, on } => match on {
-            Selector::Everything() => {
-                return Err(SchedulerError::SemanticError(
-                    "Cannot apply device to everything".to_string(),
-                ));
-            }
+            Selector::Everything() => Err(SchedulerError::SemanticError(
+                "Cannot apply device to everything".to_string(),
+            )),
             Selector::Selection(selection) => {
+                let mut changed = false;
                 for func in selection {
-                    let func = interp_expr(module, func, stringtab, env, functions)?;
-                    add_device(module, device.clone(), func.as_functions(functions)?);
+                    let (func, modified) = interp_expr(pm, func, stringtab, env, functions)?;
+                    changed |= modified;
+                    add_device(pm, device.clone(), func.as_functions(functions)?);
                 }
+                Ok(changed)
             }
         },
     }
-    Ok(())
 }
 
 fn interp_expr(
-    module: &mut Module,
+    pm: &mut PassManager,
     expr: &ScheduleExp,
     stringtab: &mut StringTable,
     env: &mut Env<usize, Value>,
-    functions: &mut JunoFunctions,
-) -> Result<Value, SchedulerError> {
+    functions: &JunoFunctions,
+) -> Result<(Value, bool), SchedulerError> {
     match expr {
         ScheduleExp::Variable { var } => {
             let var_id = stringtab.lookup_string(var.clone());
             match env.lookup(&var_id) {
                 None => Err(SchedulerError::UndefinedVariable(var.clone())),
-                Some(v) => Ok(v.clone()),
+                Some(v) => Ok((v.clone(), false)),
             }
         }
-        ScheduleExp::Integer { val } => Ok(Value::Integer { val: *val }),
-        ScheduleExp::Boolean { val } => Ok(Value::Boolean { val: *val }),
+        ScheduleExp::Integer { val } => Ok((Value::Integer { val: *val }, false)),
+        ScheduleExp::Boolean { val } => Ok((Value::Boolean { val: *val }, false)),
         ScheduleExp::Field { collect, field } => {
-            match interp_expr(module, collect, stringtab, env, functions)? {
+            let (lhs, changed) = interp_expr(pm, collect, stringtab, env, functions)?;
+            match lhs {
                 Value::Label { .. }
                 | Value::Selection { .. }
                 | Value::Everything { .. }
                 | Value::Integer { .. }
                 | Value::Boolean { .. } => Err(SchedulerError::UndefinedField(field.clone())),
                 Value::JunoFunction { func } => {
-                    match module.labels.iter().position(|s| s == field) {
+                    match pm.module.labels.iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
-                        Some(label_id) => Ok(Value::Label {
-                            labels: functions
-                                .get_function(func)
-                                .iter()
-                                .map(|f| LabelInfo {
-                                    func: *f,
-                                    label: label_id,
-                                })
-                                .collect(),
-                        }),
+                        Some(label_id) => Ok((
+                            Value::Label {
+                                labels: functions
+                                    .get_function(func)
+                                    .iter()
+                                    .map(|f| LabelInfo {
+                                        func: *f,
+                                        label: label_id,
+                                    })
+                                    .collect(),
+                            },
+                            changed,
+                        )),
                     }
                 }
                 Value::HerculesFunction { func } => {
-                    match module.labels.iter().position(|s| s == field) {
+                    match pm.module.labels.iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
-                        Some(label_id) => Ok(Value::Label {
-                            labels: vec![LabelInfo {
-                                func: func,
-                                label: label_id,
-                            }],
-                        }),
+                        Some(label_id) => Ok((
+                            Value::Label {
+                                labels: vec![LabelInfo {
+                                    func: func,
+                                    label: label_id,
+                                }],
+                            },
+                            changed,
+                        )),
                     }
                 }
                 Value::Record { fields } => match fields.get(field) {
                     None => Err(SchedulerError::UndefinedField(field.clone())),
-                    Some(v) => Ok(v.clone()),
+                    Some(v) => Ok((v.clone(), changed)),
                 },
             }
         }
         ScheduleExp::RunPass { .. } => todo!("passes not implemented"),
         ScheduleExp::Record { fields } => {
             let mut result = HashMap::new();
+            let mut changed = false;
             for (field, val) in fields {
-                result.insert(
-                    field.clone(),
-                    interp_expr(module, val, stringtab, env, functions)?,
-                );
+                let (val, modified) = interp_expr(pm, val, stringtab, env, functions)?;
+                result.insert(field.clone(), val);
+                changed |= modified;
             }
-            Ok(Value::Record { fields: result })
+            Ok((Value::Record { fields: result }, changed))
         }
         ScheduleExp::Block { body, res } => {
+            let mut changed = false;
+
             env.open_scope();
             for command in body {
-                schedule_interpret(module, command, stringtab, env, functions)?;
+                changed |= schedule_interpret(pm, command, stringtab, env, functions)?;
             }
-            let res = interp_expr(module, res, stringtab, env, functions);
+            let (res, modified) = interp_expr(pm, res, stringtab, env, functions)?;
             env.close_scope();
-            res
+
+            Ok((res, changed || modified))
         }
         ScheduleExp::Selection { selection } => match selection {
-            Selector::Everything() => Ok(Value::Everything {}),
+            Selector::Everything() => Ok((Value::Everything {}, false)),
             Selector::Selection(selection) => {
                 let mut values = vec![];
+                let mut changed = false;
                 for e in selection {
-                    values.push(interp_expr(module, e, stringtab, env, functions)?);
+                    let (val, modified) = interp_expr(pm, e, stringtab, env, functions)?;
+                    values.push(val);
+                    changed |= modified;
                 }
-                Ok(Value::Selection { selection: values })
+                Ok((Value::Selection { selection: values }, changed))
             }
         },
     }
 }
 
-fn add_schedule(module: &mut Module, sched: Schedule, label_ids: Vec<LabelInfo>) {
+fn add_schedule(pm: &mut PassManager, sched: Schedule, label_ids: Vec<LabelInfo>) {
     for LabelInfo { func, label } in label_ids {
-        let nodes = module.functions[func.idx()]
+        let nodes = pm.module.functions[func.idx()]
             .labels
             .iter()
             .enumerate()
@@ -321,13 +705,13 @@ fn add_schedule(module: &mut Module, sched: Schedule, label_ids: Vec<LabelInfo>)
             .map(|(i, ls)| i)
             .collect::<Vec<_>>();
         for node in nodes {
-            module.functions[func.idx()].schedules[node].push(sched.clone());
+            pm.module.functions[func.idx()].schedules[node].push(sched.clone());
         }
     }
 }
 
-fn add_device(module: &mut Module, device: Device, funcs: Vec<FunctionID>) {
+fn add_device(pm: &mut PassManager, device: Device, funcs: Vec<FunctionID>) {
     for func in funcs {
-        module.functions[func.idx()].device = Some(device.clone());
+        pm.module.functions[func.idx()].device = Some(device.clone());
     }
 }
-- 
GitLab


From 66a4b383ef789c82e72037b7994b64a92217dc9d Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Fri, 10 Jan 2025 16:51:45 -0600
Subject: [PATCH 23/46] Start handling passes

---
 juno_scheduler/src/ir.rs |  2 +-
 juno_scheduler/src/pm.rs | 45 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 77ea773f..6ba2ce2b 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -2,7 +2,7 @@ extern crate hercules_ir;
 
 use self::hercules_ir::ir::{Device, Schedule};
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone)]
 pub enum Pass {
     DCE,
     CCP,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 71265461..ae7dfa26 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -656,7 +656,41 @@ fn interp_expr(
                 },
             }
         }
-        ScheduleExp::RunPass { .. } => todo!("passes not implemented"),
+        ScheduleExp::RunPass { pass, args, on } => {
+            let mut changed = false;
+            let mut arg_vals = vec![];
+            for arg in args {
+                let (val, modified) = interp_expr(pm, arg, stringtab, env, functions)?;
+                arg_vals.push(val);
+                changed |= modified;
+            }
+
+            let selection = match on {
+                Selector::Everything() => None,
+                Selector::Selection(selection) => {
+                    let mut locs = vec![];
+                    let mut everything = false;
+                    for loc in selection {
+                        let (val, modified) = interp_expr(pm, loc, stringtab, env, functions)?;
+                        changed |= modified;
+                        if val.is_everything() {
+                            everything = true;
+                            break;
+                        }
+                        locs.extend(val.as_locations(functions)?);
+                    }
+                    if everything {
+                        None
+                    } else {
+                        Some(locs)
+                    }
+                }
+            };
+
+            let (res, modified) = run_pass(pm, *pass, arg_vals, selection)?;
+            changed |= modified;
+            Ok((res, changed))
+        }
         ScheduleExp::Record { fields } => {
             let mut result = HashMap::new();
             let mut changed = false;
@@ -715,3 +749,12 @@ fn add_device(pm: &mut PassManager, device: Device, funcs: Vec<FunctionID>) {
         pm.module.functions[func.idx()].device = Some(device.clone());
     }
 }
+
+fn run_pass(
+    pm: &mut PassManager,
+    pass: crate::ir::Pass,
+    args: Vec<Value>,
+    selection: Option<Vec<CodeLocation>>,
+) -> Result<(Value, bool), SchedulerError> {
+    todo!()
+}
-- 
GitLab


From 8d8e308691dd71477ab4556e99b1353373cf9ab3 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Sat, 11 Jan 2025 15:26:58 -0600
Subject: [PATCH 24/46] Handle devices and support schedules

---
 juno_scheduler/src/compile.rs | 88 ++++++++++++++++++++++++-----------
 1 file changed, 61 insertions(+), 27 deletions(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 7c44154f..0b03fab8 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -71,31 +71,55 @@ struct MacroInfo {
     def: ir::ScheduleExp,
 }
 
-impl FromStr for ir::Pass {
+enum Appliable {
+    Pass(ir::Pass),
+    Schedule(Schedule),
+    Device(Device),
+}
+
+impl Appliable {
+    fn num_args(&self) -> usize {
+        match self {
+            Appliable::Pass(pass) => pass.num_args(),
+            // Schedules and devices do not arguments (at the moment)
+            _ => 0,
+        }
+    }
+}
+
+impl FromStr for Appliable {
     type Err = String;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
-            "ccp" => Ok(ir::Pass::CCP),
-            "dce" => Ok(ir::Pass::DCE),
-            "delete-uncalled" => Ok(ir::Pass::DeleteUncalled),
-            "fork-guard-elim" => Ok(ir::Pass::ForkGuardElim),
-            "fork-split" => Ok(ir::Pass::ForkSplit),
-            "forkify" => Ok(ir::Pass::Forkify),
-            "gvn" => Ok(ir::Pass::GVN),
-            "infer-schedules" => Ok(ir::Pass::InferSchedules),
-            "inline" => Ok(ir::Pass::Inline),
-            "ip-sroa" | "interprocedural-sroa" => Ok(ir::Pass::InterproceduralSROA),
-            "outline" => Ok(ir::Pass::Outline),
-            "phi-elim" => Ok(ir::Pass::PhiElim),
-            "predication" => Ok(ir::Pass::Predication),
+            "ccp" => Ok(Appliable::Pass(ir::Pass::CCP)),
+            "dce" => Ok(Appliable::Pass(ir::Pass::DCE)),
+            "delete-uncalled" => Ok(Appliable::Pass(ir::Pass::DeleteUncalled)),
+            "fork-guard-elim" => Ok(Appliable::Pass(ir::Pass::ForkGuardElim)),
+            "fork-split" => Ok(Appliable::Pass(ir::Pass::ForkSplit)),
+            "forkify" => Ok(Appliable::Pass(ir::Pass::Forkify)),
+            "gvn" => Ok(Appliable::Pass(ir::Pass::GVN)),
+            "infer-schedules" => Ok(Appliable::Pass(ir::Pass::InferSchedules)),
+            "inline" => Ok(Appliable::Pass(ir::Pass::Inline)),
+            "ip-sroa" | "interprocedural-sroa" => {
+                Ok(Appliable::Pass(ir::Pass::InterproceduralSROA))
+            }
+            "outline" => Ok(Appliable::Pass(ir::Pass::Outline)),
+            "phi-elim" => Ok(Appliable::Pass(ir::Pass::PhiElim)),
+            "predication" => Ok(Appliable::Pass(ir::Pass::Predication)),
             "reference" | "legalize-reference-semantics" => {
-                Ok(ir::Pass::LegalizeReferenceSemantics)
+                Ok(Appliable::Pass(ir::Pass::LegalizeReferenceSemantics))
             }
-            "sroa" => Ok(ir::Pass::SROA),
-            "unforkify" => Ok(ir::Pass::Unforkify),
-            "verify" => Ok(ir::Pass::Verify),
-            "xdot" => Ok(ir::Pass::Xdot),
+            "sroa" => Ok(Appliable::Pass(ir::Pass::SROA)),
+            "unforkify" => Ok(Appliable::Pass(ir::Pass::Unforkify)),
+            "verify" => Ok(Appliable::Pass(ir::Pass::Verify)),
+            "xdot" => Ok(Appliable::Pass(ir::Pass::Xdot)),
+
+            "cpu" | "llvm" => Ok(Appliable::Device(Device::LLVM)),
+            "gpu" | "nvvm" => Ok(Appliable::Device(Device::NVVM)),
+            "host" | "rust" | "rust-async" => Ok(Appliable::Device(Device::AsyncRust)),
+
+            // TODO: Schedules
             _ => Err(s.to_string()),
         }
     }
@@ -252,15 +276,15 @@ fn compile_expr(
             args,
             selection,
         } => {
-            let pass: ir::Pass = lexer
+            let func: Appliable = lexer
                 .span_str(name)
                 .to_lowercase()
                 .parse()
                 .map_err(|s| ScheduleCompilerError::NoSuchPass(s, lexer.line_col(name)))?;
 
-            if args.len() != pass.num_args() {
+            if args.len() != func.num_args() {
                 return Err(ScheduleCompilerError::IncorrectArguments {
-                    expected: pass.num_args(),
+                    expected: func.num_args(),
                     actual: args.len(),
                     loc: lexer.line_col(span),
                 });
@@ -273,11 +297,21 @@ fn compile_expr(
 
             let selection = compile_selector(selection, lexer, macrostab, macros)?;
 
-            Ok(ExprResult::Expr(ir::ScheduleExp::RunPass {
-                pass,
-                args: arg_vals,
-                on: selection,
-            }))
+            match func {
+                Appliable::Pass(pass) => Ok(ExprResult::Expr(ir::ScheduleExp::RunPass {
+                    pass,
+                    args: arg_vals,
+                    on: selection,
+                })),
+                Appliable::Schedule(sched) => Ok(ExprResult::Stmt(ir::ScheduleStmt::AddSchedule {
+                    sched,
+                    on: selection,
+                })),
+                Appliable::Device(device) => Ok(ExprResult::Stmt(ir::ScheduleStmt::AddDevice {
+                    device,
+                    on: selection,
+                })),
+            }
         }
         parser::Expr::Macro {
             span,
-- 
GitLab


From f160d88ee58ae6644254514a3730a2fd0221ac05 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Sun, 12 Jan 2025 15:47:18 -0600
Subject: [PATCH 25/46] Refactor pass manager to make editors easier

---
 hercules_ir/src/callgraph.rs   |  5 +--
 hercules_ir/src/collections.rs | 15 +++----
 hercules_ir/src/ir.rs          |  1 -
 hercules_ir/src/parse.rs       |  1 -
 hercules_ir/src/typecheck.rs   | 12 ++----
 hercules_ir/src/verify.rs      |  8 +++-
 hercules_opt/src/pass.rs       | 16 ++++++--
 juno_scheduler/src/pm.rs       | 72 +++++++++++++++++++++++++---------
 8 files changed, 87 insertions(+), 43 deletions(-)

diff --git a/hercules_ir/src/callgraph.rs b/hercules_ir/src/callgraph.rs
index 3a8e6316..834cbbf8 100644
--- a/hercules_ir/src/callgraph.rs
+++ b/hercules_ir/src/callgraph.rs
@@ -79,10 +79,9 @@ impl CallGraph {
 /*
  * Top level function to calculate the call graph of a Hercules module.
  */
-pub fn callgraph(module: &Module) -> CallGraph {
+pub fn callgraph(functions: &Vec<Function>) -> CallGraph {
     // Step 1: collect the functions called in each function.
-    let callee_functions: Vec<Vec<FunctionID>> = module
-        .functions
+    let callee_functions: Vec<Vec<FunctionID>> = functions
         .iter()
         .map(|func| {
             let mut called: Vec<_> = func
diff --git a/hercules_ir/src/collections.rs b/hercules_ir/src/collections.rs
index 8bb1b359..9f421221 100644
--- a/hercules_ir/src/collections.rs
+++ b/hercules_ir/src/collections.rs
@@ -135,7 +135,8 @@ impl Semilattice for CollectionObjectLattice {
  * Top level function to analyze collection objects in a Hercules module.
  */
 pub fn collection_objects(
-    module: &Module,
+    functions: &Vec<Function>,
+    types: &Vec<Type>,
     reverse_postorders: &Vec<Vec<NodeID>>,
     typing: &ModuleTyping,
     callgraph: &CallGraph,
@@ -146,7 +147,7 @@ pub fn collection_objects(
     let topo = callgraph.topo();
 
     for func_id in topo {
-        let func = &module.functions[func_id.idx()];
+        let func = &functions[func_id.idx()];
         let typing = &typing[func_id.idx()];
         let reverse_postorder = &reverse_postorders[func_id.idx()];
 
@@ -156,14 +157,14 @@ pub fn collection_objects(
             .param_types
             .iter()
             .enumerate()
-            .filter(|(_, ty_id)| !module.types[ty_id.idx()].is_primitive())
+            .filter(|(_, ty_id)| !types[ty_id.idx()].is_primitive())
             .map(|(idx, _)| CollectionObjectOrigin::Parameter(idx));
         let other_origins = func
             .nodes
             .iter()
             .enumerate()
             .filter_map(|(idx, node)| match node {
-                Node::Constant { id: _ } if !module.types[typing[idx].idx()].is_primitive() => {
+                Node::Constant { id: _ } if !types[typing[idx].idx()].is_primitive() => {
                     Some(CollectionObjectOrigin::Constant(NodeID::new(idx)))
                 }
                 Node::Call {
@@ -185,7 +186,7 @@ pub fn collection_objects(
                     // this is determined later.
                     Some(CollectionObjectOrigin::Call(NodeID::new(idx)))
                 }
-                Node::Undef { ty: _ } if !module.types[typing[idx].idx()].is_primitive() => {
+                Node::Undef { ty: _ } if !types[typing[idx].idx()].is_primitive() => {
                     Some(CollectionObjectOrigin::Undef(NodeID::new(idx)))
                 }
                 _ => None,
@@ -255,7 +256,7 @@ pub fn collection_objects(
                     function: callee,
                     dynamic_constants: _,
                     args: _,
-                } if !module.types[typing[id.idx()].idx()].is_primitive() => {
+                } if !types[typing[id.idx()].idx()].is_primitive() => {
                     let new_obj = origins
                         .iter()
                         .position(|origin| *origin == CollectionObjectOrigin::Call(id))
@@ -285,7 +286,7 @@ pub fn collection_objects(
                 Node::Read {
                     collect: _,
                     indices: _,
-                } if !module.types[typing[id.idx()].idx()].is_primitive() => inputs[0].clone(),
+                } if !types[typing[id.idx()].idx()].is_primitive() => inputs[0].clone(),
                 Node::Write {
                     collect: _,
                     data: _,
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 84284d14..25063f5d 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -12,7 +12,6 @@ use self::bitvec::prelude::*;
 use self::serde::Deserialize;
 use self::serde::Serialize;
 
-
 use crate::*;
 
 /*
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 18ea6eeb..47863058 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -6,7 +6,6 @@ use std::str::FromStr;
 
 use crate::*;
 
-
 /*
  * TODO: This parsing code was written before the generic build API was created.
  * As a result, this parsing code duplicates much of the interning logic the
diff --git a/hercules_ir/src/typecheck.rs b/hercules_ir/src/typecheck.rs
index b7e1a7a6..2ad42d5f 100644
--- a/hercules_ir/src/typecheck.rs
+++ b/hercules_ir/src/typecheck.rs
@@ -81,20 +81,16 @@ pub type ModuleTyping = Vec<Vec<TypeID>>;
  * Returns a type for every node in every function.
  */
 pub fn typecheck(
-    module: &mut Module,
+    functions: &Vec<Function>,
+    types: &mut Vec<Type>,
+    constants: &Vec<Constant>,
+    dynamic_constants: &mut Vec<DynamicConstant>,
     reverse_postorders: &Vec<Vec<NodeID>>,
 ) -> Result<ModuleTyping, String> {
     // Step 1: assemble a reverse type map. This is needed to get or create the
     // ID of potentially new types. Break down module into references to
     // individual elements at this point, so that borrows don't overlap each
     // other.
-    let Module {
-        ref functions,
-        ref mut types,
-        ref constants,
-        ref mut dynamic_constants,
-        ..
-    } = module;
     let mut reverse_type_map: HashMap<Type, TypeID> = types
         .iter()
         .enumerate()
diff --git a/hercules_ir/src/verify.rs b/hercules_ir/src/verify.rs
index 18ad92c3..9acd74e5 100644
--- a/hercules_ir/src/verify.rs
+++ b/hercules_ir/src/verify.rs
@@ -31,7 +31,13 @@ pub fn verify(
     let reverse_postorders: Vec<_> = def_uses.iter().map(reverse_postorder).collect();
 
     // Typecheck the module.
-    let typing = typecheck(module, &reverse_postorders)?;
+    let typing = typecheck(
+        &module.functions,
+        &mut module.types,
+        &module.constants,
+        &mut module.dynamic_constants,
+        &reverse_postorders,
+    )?;
 
     // Assemble fork join maps for module.
     let subgraphs: Vec<_> = zip(module.functions.iter(), def_uses.iter())
diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs
index 967441bc..f36d8ae7 100644
--- a/hercules_opt/src/pass.rs
+++ b/hercules_opt/src/pass.rs
@@ -128,7 +128,14 @@ impl PassManager {
         if self.typing.is_none() {
             self.make_reverse_postorders();
             self.typing = Some(
-                typecheck(&mut self.module, self.reverse_postorders.as_ref().unwrap()).unwrap(),
+                typecheck(
+                    &self.module.functions,
+                    &mut self.module.types,
+                    &self.module.constants,
+                    &mut self.module.dynamic_constants,
+                    self.reverse_postorders.as_ref().unwrap(),
+                )
+                .unwrap(),
             );
         }
     }
@@ -265,7 +272,8 @@ impl PassManager {
             let typing = self.typing.as_ref().unwrap();
             let callgraph = self.callgraph.as_ref().unwrap();
             self.collection_objects = Some(collection_objects(
-                &self.module,
+                &self.module.functions,
+                &self.module.types,
                 reverse_postorders,
                 typing,
                 callgraph,
@@ -275,7 +283,7 @@ impl PassManager {
 
     pub fn make_callgraph(&mut self) {
         if self.callgraph.is_none() {
-            self.callgraph = Some(callgraph(&self.module));
+            self.callgraph = Some(callgraph(&self.module.functions));
         }
     }
 
@@ -867,7 +875,7 @@ impl PassManager {
                             &dynamic_constants_ref,
                             &types_ref,
                             &labels_ref,
-                            &def_uses[idx]
+                            &def_uses[idx],
                         );
                         infer_parallel_reduce(
                             &mut editor,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index ae7dfa26..57db9c40 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -10,6 +10,7 @@ use hercules_ir::*;
 use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
 
+use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
 use std::env::temp_dir;
 use std::fmt;
@@ -161,7 +162,11 @@ impl fmt::Display for SchedulerError {
 
 #[derive(Debug)]
 struct PassManager {
-    module: Module,
+    functions: Vec<Function>,
+    types: RefCell<Vec<Type>>,
+    constants: RefCell<Vec<Constant>>,
+    dynamic_constants: RefCell<Vec<DynamicConstant>>,
+    labels: RefCell<Vec<String>>,
 
     // Cached analysis results.
     pub def_uses: Option<Vec<ImmutableDefUseMap>>,
@@ -182,8 +187,19 @@ struct PassManager {
 
 impl PassManager {
     fn new(module: Module) -> Self {
+        let Module {
+            functions,
+            types,
+            constants,
+            dynamic_constants,
+            labels,
+        } = module;
         PassManager {
-            module,
+            functions,
+            types: RefCell::new(types),
+            constants: RefCell::new(constants),
+            dynamic_constants: RefCell::new(dynamic_constants),
+            labels: RefCell::new(labels),
             def_uses: None,
             reverse_postorders: None,
             typing: None,
@@ -203,7 +219,7 @@ impl PassManager {
 
     pub fn make_def_uses(&mut self) {
         if self.def_uses.is_none() {
-            self.def_uses = Some(self.module.functions.iter().map(def_use).collect());
+            self.def_uses = Some(self.functions.iter().map(def_use).collect());
         }
     }
 
@@ -225,7 +241,14 @@ impl PassManager {
         if self.typing.is_none() {
             self.make_reverse_postorders();
             self.typing = Some(
-                typecheck(&mut self.module, self.reverse_postorders.as_ref().unwrap()).unwrap(),
+                typecheck(
+                    &self.functions,
+                    &mut self.types.borrow_mut(),
+                    &self.constants.borrow(),
+                    &mut self.dynamic_constants.borrow_mut(),
+                    self.reverse_postorders.as_ref().unwrap(),
+                )
+                .unwrap(),
             );
         }
     }
@@ -234,7 +257,7 @@ impl PassManager {
         if self.control_subgraphs.is_none() {
             self.make_def_uses();
             self.control_subgraphs = Some(
-                zip(&self.module.functions, self.def_uses.as_ref().unwrap())
+                zip(&self.functions, self.def_uses.as_ref().unwrap())
                     .map(|(function, def_use)| control_subgraph(function, def_use))
                     .collect(),
             );
@@ -261,7 +284,7 @@ impl PassManager {
             self.postdoms = Some(
                 zip(
                     self.control_subgraphs.as_ref().unwrap().iter(),
-                    self.module.functions.iter(),
+                    self.functions.iter(),
                 )
                 .map(|(subgraph, function)| dominator(subgraph, NodeID::new(function.nodes.len())))
                 .collect(),
@@ -274,7 +297,7 @@ impl PassManager {
             self.make_control_subgraphs();
             self.fork_join_maps = Some(
                 zip(
-                    self.module.functions.iter(),
+                    self.functions.iter(),
                     self.control_subgraphs.as_ref().unwrap().iter(),
                 )
                 .map(|(function, subgraph)| fork_join_map(function, subgraph))
@@ -289,7 +312,7 @@ impl PassManager {
             self.make_fork_join_maps();
             self.fork_join_nests = Some(
                 zip(
-                    self.module.functions.iter(),
+                    self.functions.iter(),
                     zip(
                         self.doms.as_ref().unwrap().iter(),
                         self.fork_join_maps.as_ref().unwrap().iter(),
@@ -326,7 +349,7 @@ impl PassManager {
             self.make_def_uses();
             let def_uses = self.def_uses.as_ref().unwrap().iter();
             self.reduce_cycles = Some(
-                zip(self.module.functions.iter(), def_uses)
+                zip(self.functions.iter(), def_uses)
                     .map(|(function, def_use)| reduce_cycles(function, def_use))
                     .collect(),
             );
@@ -339,7 +362,7 @@ impl PassManager {
             self.make_fork_join_maps();
             self.data_nodes_in_fork_joins = Some(
                 zip(
-                    self.module.functions.iter(),
+                    self.functions.iter(),
                     zip(
                         self.def_uses.as_ref().unwrap().iter(),
                         self.fork_join_maps.as_ref().unwrap().iter(),
@@ -362,7 +385,8 @@ impl PassManager {
             let typing = self.typing.as_ref().unwrap();
             let callgraph = self.callgraph.as_ref().unwrap();
             self.collection_objects = Some(collection_objects(
-                &self.module,
+                &self.functions,
+                &self.types.borrow(),
                 reverse_postorders,
                 typing,
                 callgraph,
@@ -372,7 +396,7 @@ impl PassManager {
 
     pub fn make_callgraph(&mut self) {
         if self.callgraph.is_none() {
-            self.callgraph = Some(callgraph(&self.module));
+            self.callgraph = Some(callgraph(&self.functions));
         }
     }
 
@@ -384,7 +408,11 @@ impl PassManager {
         self.make_callgraph();
 
         let PassManager {
-            module,
+            functions,
+            types,
+            constants,
+            dynamic_constants,
+            labels,
             reverse_postorders: Some(reverse_postorders),
             typing: Some(typing),
             control_subgraphs: Some(control_subgraphs),
@@ -400,6 +428,14 @@ impl PassManager {
             });
         };
 
+        let module = Module {
+            functions,
+            types: types.into_inner(),
+            constants: constants.into_inner(),
+            dynamic_constants: dynamic_constants.into_inner(),
+            labels: labels.into_inner(),
+        };
+
         let devices = device_placement(&module.functions, &callgraph);
 
         let mut rust_rt = String::new();
@@ -619,7 +655,7 @@ fn interp_expr(
                 | Value::Integer { .. }
                 | Value::Boolean { .. } => Err(SchedulerError::UndefinedField(field.clone())),
                 Value::JunoFunction { func } => {
-                    match pm.module.labels.iter().position(|s| s == field) {
+                    match pm.labels.borrow().iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
                         Some(label_id) => Ok((
                             Value::Label {
@@ -637,7 +673,7 @@ fn interp_expr(
                     }
                 }
                 Value::HerculesFunction { func } => {
-                    match pm.module.labels.iter().position(|s| s == field) {
+                    match pm.labels.borrow().iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
                         Some(label_id) => Ok((
                             Value::Label {
@@ -731,7 +767,7 @@ fn interp_expr(
 
 fn add_schedule(pm: &mut PassManager, sched: Schedule, label_ids: Vec<LabelInfo>) {
     for LabelInfo { func, label } in label_ids {
-        let nodes = pm.module.functions[func.idx()]
+        let nodes = pm.functions[func.idx()]
             .labels
             .iter()
             .enumerate()
@@ -739,14 +775,14 @@ fn add_schedule(pm: &mut PassManager, sched: Schedule, label_ids: Vec<LabelInfo>
             .map(|(i, ls)| i)
             .collect::<Vec<_>>();
         for node in nodes {
-            pm.module.functions[func.idx()].schedules[node].push(sched.clone());
+            pm.functions[func.idx()].schedules[node].push(sched.clone());
         }
     }
 }
 
 fn add_device(pm: &mut PassManager, device: Device, funcs: Vec<FunctionID>) {
     for func in funcs {
-        pm.module.functions[func.idx()].device = Some(device.clone());
+        pm.functions[func.idx()].device = Some(device.clone());
     }
 }
 
-- 
GitLab


From 1b326fc29c8a67b744f11c9b24037768fce9b507 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 10:45:54 -0600
Subject: [PATCH 26/46] Start implementing pass runner

---
 hercules_opt/src/editor.rs    |  56 ++++++++
 juno_scheduler/src/default.rs |   2 +-
 juno_scheduler/src/ir.rs      |  20 +--
 juno_scheduler/src/pm.rs      | 239 +++++++++++++++++++++++++++++++++-
 4 files changed, 303 insertions(+), 14 deletions(-)

diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index d9204573..697bfec7 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -42,6 +42,9 @@ pub struct FunctionEditor<'a> {
     // are off limits for deletion (equivalently modification) or being replaced
     // as a use.
     mutable_nodes: BitVec<u8, Lsb0>,
+    // Tracks whether this editor has been used to make any edits to the IR of
+    // this function
+    modified: bool,
 }
 
 /*
@@ -103,9 +106,57 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             labels,
             mut_def_use,
             mutable_nodes,
+            modified: false,
         }
     }
 
+    // Constructs an editor but only makes the nodes with at least one of the set of labels as
+    // mutable
+    pub fn new_labeled(
+        function: &'a mut Function,
+        function_id: FunctionID,
+        constants: &'a RefCell<Vec<Constant>>,
+        dynamic_constants: &'a RefCell<Vec<DynamicConstant>>,
+        types: &'a RefCell<Vec<Type>>,
+        labels: &'a RefCell<Vec<String>>,
+        def_use: &ImmutableDefUseMap,
+        with_labels: &HashSet<LabelID>,
+    ) -> Self {
+        let mut_def_use = (0..function.nodes.len())
+            .map(|idx| {
+                def_use
+                    .get_users(NodeID::new(idx))
+                    .into_iter()
+                    .map(|x| *x)
+                    .collect()
+            })
+            .collect();
+
+        let mut mutable_nodes = bitvec![u8, Lsb0; 0; function.nodes.len()];
+        // Add all nodes which have some label which is in the with_labels set
+        for (idx, labels) in function.labels.iter().enumerate() {
+            if !labels.is_disjoint(with_labels) {
+                mutable_nodes.set(idx, true);
+            }
+        }
+
+        FunctionEditor {
+            function,
+            function_id,
+            constants,
+            dynamic_constants,
+            types,
+            labels,
+            mut_def_use,
+            mutable_nodes,
+            modified: false,
+        }
+    }
+
+    pub fn modified(&self) -> bool {
+        self.modified
+    }
+
     pub fn edit<F>(&'b mut self, edit: F) -> bool
     where
         F: FnOnce(FunctionEdit<'a, 'b>) -> Result<FunctionEdit<'a, 'b>, FunctionEdit<'a, 'b>>,
@@ -145,6 +196,11 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
                 updated_return_type,
                 sub_edits,
             } = populated_edit;
+
+            // Step 0: determine whether the edit changed the IR by checking if
+            // any nodes were deleted, added, or updated in any way
+            editor.modified |= !deleted_nodeids.is_empty() || !added_nodeids.is_empty() || !added_and_updated_nodes.is_empty() || !added_and_updated_schedules.is_empty() || !added_and_updated_labels.is_empty();
+
             // Step 1: update the mutable def use map.
             for (u, new_users) in updated_def_use {
                 // Go through new def-use entries in order. These are either
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index f1c13516..b3ff6155 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -33,7 +33,7 @@ pub fn default_schedule() -> ScheduleStmt {
         PhiElim,
         DCE,
         Inline,
-        DeleteUncalled,
+        /*DeleteUncalled,*/
         InterproceduralSROA,
         SROA,
         PhiElim,
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 6ba2ce2b..5e68e585 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -4,22 +4,22 @@ use self::hercules_ir::ir::{Device, Schedule};
 
 #[derive(Debug, Copy, Clone)]
 pub enum Pass {
-    DCE,
     CCP,
+    DCE,
+    DeleteUncalled,
+    ForkGuardElim,
+    ForkSplit,
+    Forkify,
     GVN,
+    InferSchedules,
+    Inline,
+    InterproceduralSROA,
+    LegalizeReferenceSemantics,
+    Outline,
     PhiElim,
-    Forkify,
-    ForkGuardElim,
     Predication,
     SROA,
-    Inline,
-    Outline,
-    InterproceduralSROA,
-    DeleteUncalled,
-    ForkSplit,
     Unforkify,
-    InferSchedules,
-    LegalizeReferenceSemantics,
     Verify,
     Xdot,
 }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 57db9c40..2d1f881a 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -2,7 +2,8 @@ extern crate hercules_cg;
 extern crate hercules_opt;
 
 use self::hercules_cg::*;
-use self::hercules_opt::*;
+use self::hercules_opt::FunctionEditor;
+use self::hercules_opt::{ccp, dce, fork_split, gvn, infer_parallel_reduce, infer_parallel_fork, infer_vectorizable, infer_tight_associative, inline, interprocedural_sroa};
 use crate::ir::*;
 use crate::labels::*;
 use hercules_ir::*;
@@ -400,6 +401,29 @@ impl PassManager {
         }
     }
 
+    pub fn delete_gravestones(&mut self) {
+        for func in self.functions.iter_mut() {
+            func.delete_gravestones();
+        }
+    }
+
+    fn clear_analyses(&mut self) {
+        self.def_uses = None;
+        self.reverse_postorders = None;
+        self.typing = None;
+        self.control_subgraphs = None;
+        self.doms = None;
+        self.postdoms = None;
+        self.fork_join_maps = None;
+        self.fork_join_nests = None;
+        self.loops = None;
+        self.reduce_cycles = None;
+        self.data_nodes_in_fork_joins = None;
+        self.bbs = None;
+        self.collection_objects = None;
+        self.callgraph = None;
+    }
+
     fn codegen(mut self, output_dir: String, module_name: String) -> Result<(), SchedulerError> {
         self.make_reverse_postorders();
         self.make_typing();
@@ -786,11 +810,220 @@ fn add_device(pm: &mut PassManager, device: Device, funcs: Vec<FunctionID>) {
     }
 }
 
+#[derive(Debug, Clone)]
+enum FunctionSelection {
+    Nothing(),
+    Everything(),
+    Labels(HashSet<LabelID>),
+}
+
+impl FunctionSelection {
+    fn add_label(&mut self, label: LabelID) {
+        match self {
+            FunctionSelection::Nothing() => {
+                *self = FunctionSelection::Labels(HashSet::from([label]));
+            }
+            FunctionSelection::Everything() => {}
+            FunctionSelection::Labels(set) => {
+                set.insert(label);
+            }
+        }
+    }
+
+    fn add_everything(&mut self) {
+        *self = FunctionSelection::Everything();
+    }
+}
+
+fn build_selection<'a>(
+    pm: &'a mut PassManager,
+    selection: Option<Vec<CodeLocation>>,
+) -> Vec<Option<FunctionEditor<'a>>> {
+    // Build def uses, which are needed for the editors we'll construct
+    pm.make_def_uses();
+    let def_uses = pm.def_uses.take().unwrap();
+
+    // The results are a list of (optional) function editors, which edit the function at that index
+    let mut res = vec![];
+
+    if let Some(selection) = selection {
+        // With a selection, we first process it to identify which labels in which functions are to
+        // be selected. Then, we use that to construct the editors
+        let mut selected = vec![FunctionSelection::Nothing(); pm.functions.len()];
+
+        for loc in selection {
+            match loc {
+                CodeLocation::Label(label) => selected[label.func.idx()].add_label(label.label),
+                CodeLocation::Function(func) => selected[func.idx()].add_everything(),
+            }
+        }
+
+        for (idx, ((func, selected), def_use)) in pm.functions.iter_mut().zip(selected.iter()).zip(def_uses.iter()).enumerate() {
+            match selected {
+                FunctionSelection::Nothing() => { res.push(None); }
+                FunctionSelection::Everything() => {
+                    res.push(Some(FunctionEditor::new(
+                                func,
+                                FunctionID::new(idx),
+                                &pm.constants,
+                                &pm.dynamic_constants,
+                                &pm.types,
+                                &pm.labels,
+                                def_use,
+                            )));
+                }
+                FunctionSelection::Labels(labels) => {
+                    res.push(Some(FunctionEditor::new_labeled(
+                                func,
+                                FunctionID::new(idx),
+                                &pm.constants,
+                                &pm.dynamic_constants,
+                                &pm.types,
+                                &pm.labels,
+                                def_use,
+                                labels,
+                            )));
+                }
+            }
+        }
+    } else {
+        // If no selection is provided, we apply the optimization to everything
+        for (idx, (func, def_use)) in pm.functions.iter_mut().zip(def_uses.iter()).enumerate() {
+            res.push(Some(FunctionEditor::new(
+                func,
+                FunctionID::new(idx),
+                &pm.constants,
+                &pm.dynamic_constants,
+                &pm.types,
+                &pm.labels,
+                def_use,
+            )));
+        }
+    }
+
+    res
+}
+
 fn run_pass(
     pm: &mut PassManager,
-    pass: crate::ir::Pass,
+    pass: Pass,
     args: Vec<Value>,
     selection: Option<Vec<CodeLocation>>,
 ) -> Result<(Value, bool), SchedulerError> {
-    todo!()
+    let mut result = Value::Record { fields: HashMap::new() };
+    let mut changed = false;
+
+    match pass {
+        Pass::CCP => {
+            assert!(args.is_empty());
+            pm.make_reverse_postorders();
+            let reverse_postorders = pm.reverse_postorders.take().unwrap();
+            for (func, reverse_postorder) in build_selection(pm, selection).into_iter().zip(reverse_postorders.iter()) {
+                let Some(mut func) = func else { continue; };
+                ccp(&mut func, reverse_postorder);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::DCE => {
+            assert!(args.is_empty());
+            for func in build_selection(pm, selection) {
+                let Some(mut func) = func else { continue; };
+                dce(&mut func);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::DeleteUncalled => {
+            todo!("Delete Uncalled changes FunctionIDs, a bunch of bookkeeping is needed for the pass manager to address this")
+        }
+        Pass::ForkGuardElim => {
+            todo!("Fork Guard Elim doesn't use editor")
+        }
+        Pass::ForkSplit => {
+            assert!(args.is_empty());
+            pm.make_fork_join_maps();
+            pm.make_reduce_cycles();
+            let fork_join_maps = pm.fork_join_maps.take().unwrap();
+            let reduce_cycles = pm.reduce_cycles.take().unwrap();
+            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection).into_iter().zip(fork_join_maps.iter()).zip(reduce_cycles.iter()) {
+                let Some(mut func) = func else { continue; };
+                fork_split(&mut func, fork_join_map, reduce_cycles);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::Forkify => {
+            todo!("Forkify doesn't use editor")
+        }
+        Pass::GVN => {
+            assert!(args.is_empty());
+            for func in build_selection(pm, selection) {
+                let Some(mut func) = func else { continue; };
+                gvn(&mut func, false);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::InferSchedules => {
+            assert!(args.is_empty());
+            pm.make_fork_join_maps();
+            pm.make_reduce_cycles();
+            let fork_join_maps = pm.fork_join_maps.take().unwrap();
+            let reduce_cycles = pm.reduce_cycles.take().unwrap();
+            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection).into_iter().zip(fork_join_maps.iter()).zip(reduce_cycles.iter()) {
+                let Some(mut func) = func else { continue; };
+                infer_parallel_reduce(&mut func, fork_join_map, reduce_cycles);
+                infer_parallel_fork(&mut func, fork_join_map);
+                infer_vectorizable(&mut func, fork_join_map);
+                infer_tight_associative(&mut func, reduce_cycles);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::Inline => {
+            assert!(args.is_empty());
+            if let Some(_) = selection { 
+                return Err(SchedulerError::PassError { 
+                    pass: "inline".to_string(), 
+                    error: "must be applied to the entire module".to_string()
+                });
+            }
+
+            pm.make_callgraph();
+            let callgraph = pm.callgraph.take().unwrap();
+            let mut editors = build_selection(pm, selection).into_iter().map(|f| f.unwrap()).collect::<Vec<_>>();
+            inline(&mut editors, &callgraph);
+
+            for func in editors { changed |= func.modified(); }
+
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::InterproceduralSROA => {
+            assert!(args.is_empty());
+            if let Some(_) = selection { 
+                return Err(SchedulerError::PassError { 
+                    pass: "interproceduralSROA".to_string(), 
+                    error: "must be applied to the entire module".to_string()
+                });
+            }
+
+            let mut editors = build_selection(pm, selection).into_iter().map(|f| f.unwrap()).collect::<Vec<_>>();
+            interprocedural_sroa(&mut editors);
+
+            for func in editors { changed |= func.modified(); }
+
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        _ => todo!(),
+    }
+
+    Ok((result, changed))
 }
-- 
GitLab


From 380b2d0ab851460b854e6569e0f26118e225dde6 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 11:34:36 -0600
Subject: [PATCH 27/46] Finished adding most passes

---
 juno_scheduler/src/compile.rs |   2 +-
 juno_scheduler/src/pm.rs      | 301 ++++++++++++++++++++++++++++------
 2 files changed, 255 insertions(+), 48 deletions(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index edf26e39..252a959f 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -95,7 +95,7 @@ impl FromStr for Appliable {
             "ccp" => Ok(Appliable::Pass(ir::Pass::CCP)),
             "dce" => Ok(Appliable::Pass(ir::Pass::DCE)),
             "delete-uncalled" => Ok(Appliable::Pass(ir::Pass::DeleteUncalled)),
-            "float-collections" => Ok(Appliable::Pass(ir::Pass::FloatCollections)),
+            "float-collections" | "collections" => Ok(Appliable::Pass(ir::Pass::FloatCollections)),
             "fork-guard-elim" => Ok(Appliable::Pass(ir::Pass::ForkGuardElim)),
             "fork-split" => Ok(Appliable::Pass(ir::Pass::ForkSplit)),
             "forkify" => Ok(Appliable::Pass(ir::Pass::Forkify)),
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index b7fa0fb5..0e603bf2 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1,12 +1,13 @@
-extern crate hercules_cg;
-extern crate hercules_opt;
-
-use self::hercules_cg::*;
-use self::hercules_opt::FunctionEditor;
-use self::hercules_opt::{ccp, dce, fork_split, gvn, infer_parallel_reduce, infer_parallel_fork, infer_vectorizable, infer_tight_associative, inline, interprocedural_sroa};
 use crate::ir::*;
 use crate::labels::*;
+use hercules_cg::*;
 use hercules_ir::*;
+use hercules_opt::FunctionEditor;
+use hercules_opt::{
+    ccp, dce, float_collections, fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce,
+    infer_tight_associative, infer_vectorizable, inline, interprocedural_sroa, phi_elim,
+    predication, sroa, unforkify,
+};
 
 use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
@@ -32,6 +33,7 @@ pub enum Value {
     Boolean { val: bool },
 }
 
+#[derive(Debug, Copy, Clone)]
 enum CodeLocation {
     Label(LabelInfo),
     Function(FunctionID),
@@ -856,31 +858,39 @@ fn build_selection<'a>(
             }
         }
 
-        for (idx, ((func, selected), def_use)) in pm.functions.iter_mut().zip(selected.iter()).zip(def_uses.iter()).enumerate() {
+        for (idx, ((func, selected), def_use)) in pm
+            .functions
+            .iter_mut()
+            .zip(selected.iter())
+            .zip(def_uses.iter())
+            .enumerate()
+        {
             match selected {
-                FunctionSelection::Nothing() => { res.push(None); }
+                FunctionSelection::Nothing() => {
+                    res.push(None);
+                }
                 FunctionSelection::Everything() => {
                     res.push(Some(FunctionEditor::new(
-                                func,
-                                FunctionID::new(idx),
-                                &pm.constants,
-                                &pm.dynamic_constants,
-                                &pm.types,
-                                &pm.labels,
-                                def_use,
-                            )));
+                        func,
+                        FunctionID::new(idx),
+                        &pm.constants,
+                        &pm.dynamic_constants,
+                        &pm.types,
+                        &pm.labels,
+                        def_use,
+                    )));
                 }
                 FunctionSelection::Labels(labels) => {
                     res.push(Some(FunctionEditor::new_labeled(
-                                func,
-                                FunctionID::new(idx),
-                                &pm.constants,
-                                &pm.dynamic_constants,
-                                &pm.types,
-                                &pm.labels,
-                                def_use,
-                                labels,
-                            )));
+                        func,
+                        FunctionID::new(idx),
+                        &pm.constants,
+                        &pm.dynamic_constants,
+                        &pm.types,
+                        &pm.labels,
+                        def_use,
+                        labels,
+                    )));
                 }
             }
         }
@@ -908,7 +918,9 @@ fn run_pass(
     args: Vec<Value>,
     selection: Option<Vec<CodeLocation>>,
 ) -> Result<(Value, bool), SchedulerError> {
-    let mut result = Value::Record { fields: HashMap::new() };
+    let mut result = Value::Record {
+        fields: HashMap::new(),
+    };
     let mut changed = false;
 
     match pass {
@@ -916,8 +928,13 @@ fn run_pass(
             assert!(args.is_empty());
             pm.make_reverse_postorders();
             let reverse_postorders = pm.reverse_postorders.take().unwrap();
-            for (func, reverse_postorder) in build_selection(pm, selection).into_iter().zip(reverse_postorders.iter()) {
-                let Some(mut func) = func else { continue; };
+            for (func, reverse_postorder) in build_selection(pm, selection)
+                .into_iter()
+                .zip(reverse_postorders.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
                 ccp(&mut func, reverse_postorder);
                 changed |= func.modified();
             }
@@ -927,7 +944,9 @@ fn run_pass(
         Pass::DCE => {
             assert!(args.is_empty());
             for func in build_selection(pm, selection) {
-                let Some(mut func) = func else { continue; };
+                let Some(mut func) = func else {
+                    continue;
+                };
                 dce(&mut func);
                 changed |= func.modified();
             }
@@ -937,6 +956,35 @@ fn run_pass(
         Pass::DeleteUncalled => {
             todo!("Delete Uncalled changes FunctionIDs, a bunch of bookkeeping is needed for the pass manager to address this")
         }
+        Pass::FloatCollections => {
+            assert!(args.is_empty());
+            if let Some(_) = selection {
+                return Err(SchedulerError::PassError {
+                    pass: "floatCollections".to_string(),
+                    error: "must be applied to the entire module".to_string(),
+                });
+            }
+
+            pm.make_typing();
+            pm.make_callgraph();
+            let typing = pm.typing.take().unwrap();
+            let callgraph = pm.callgraph.take().unwrap();
+
+            let devices = device_placement(&pm.functions, &callgraph);
+
+            let mut editors = build_selection(pm, selection)
+                .into_iter()
+                .map(|f| f.unwrap())
+                .collect::<Vec<_>>();
+            float_collections(&mut editors, &typing, &callgraph, &devices);
+
+            for func in editors {
+                changed |= func.modified();
+            }
+
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
         Pass::ForkGuardElim => {
             todo!("Fork Guard Elim doesn't use editor")
         }
@@ -946,8 +994,14 @@ fn run_pass(
             pm.make_reduce_cycles();
             let fork_join_maps = pm.fork_join_maps.take().unwrap();
             let reduce_cycles = pm.reduce_cycles.take().unwrap();
-            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection).into_iter().zip(fork_join_maps.iter()).zip(reduce_cycles.iter()) {
-                let Some(mut func) = func else { continue; };
+            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection)
+                .into_iter()
+                .zip(fork_join_maps.iter())
+                .zip(reduce_cycles.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
                 fork_split(&mut func, fork_join_map, reduce_cycles);
                 changed |= func.modified();
             }
@@ -957,10 +1011,85 @@ fn run_pass(
         Pass::Forkify => {
             todo!("Forkify doesn't use editor")
         }
+        Pass::GCM => {
+            assert!(args.is_empty());
+            if let Some(_) = selection {
+                return Err(SchedulerError::PassError {
+                    pass: "gcm".to_string(),
+                    error: "must be applied to the entire module".to_string(),
+                });
+            }
+
+            loop {
+                pm.make_def_uses();
+                pm.make_reverse_postorders();
+                pm.make_typing();
+                pm.make_control_subgraphs();
+                pm.make_doms();
+                pm.make_fork_join_maps();
+                pm.make_loops();
+                pm.make_collection_objects();
+
+                let def_uses = pm.def_uses.take().unwrap();
+                let reverse_postorders = pm.reverse_postorders.take().unwrap();
+                let typing = pm.typing.take().unwrap();
+                let doms = pm.doms.take().unwrap();
+                let fork_join_maps = pm.fork_join_maps.take().unwrap();
+                let loops = pm.loops.take().unwrap();
+                let control_subgraphs = pm.control_subgraphs.take().unwrap();
+                let collection_objects = pm.collection_objects.take().unwrap();
+
+                let mut bbs = vec![];
+
+                for (
+                    (
+                        (
+                            ((((mut func, def_use), reverse_postorder), typing), control_subgraph),
+                            doms,
+                        ),
+                        fork_join_map,
+                    ),
+                    loops,
+                ) in build_selection(pm, selection.clone())
+                    .into_iter()
+                    .map(|f| f.unwrap())
+                    .zip(def_uses.iter())
+                    .zip(reverse_postorders.iter())
+                    .zip(typing.iter())
+                    .zip(control_subgraphs.iter())
+                    .zip(doms.iter())
+                    .zip(fork_join_maps.iter())
+                    .zip(loops.iter())
+                {
+                    if let Some(bb) = gcm(
+                        &mut func,
+                        def_use,
+                        reverse_postorder,
+                        typing,
+                        control_subgraph,
+                        doms,
+                        fork_join_map,
+                        loops,
+                        &collection_objects,
+                    ) {
+                        bbs.push(bb);
+                    }
+                    changed |= func.modified();
+                }
+                pm.delete_gravestones();
+                pm.clear_analyses();
+                if bbs.len() == pm.functions.len() {
+                    pm.bbs = Some(bbs);
+                    break;
+                }
+            }
+        }
         Pass::GVN => {
             assert!(args.is_empty());
             for func in build_selection(pm, selection) {
-                let Some(mut func) = func else { continue; };
+                let Some(mut func) = func else {
+                    continue;
+                };
                 gvn(&mut func, false);
                 changed |= func.modified();
             }
@@ -973,8 +1102,14 @@ fn run_pass(
             pm.make_reduce_cycles();
             let fork_join_maps = pm.fork_join_maps.take().unwrap();
             let reduce_cycles = pm.reduce_cycles.take().unwrap();
-            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection).into_iter().zip(fork_join_maps.iter()).zip(reduce_cycles.iter()) {
-                let Some(mut func) = func else { continue; };
+            for ((func, fork_join_map), reduce_cycles) in build_selection(pm, selection)
+                .into_iter()
+                .zip(fork_join_maps.iter())
+                .zip(reduce_cycles.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
                 infer_parallel_reduce(&mut func, fork_join_map, reduce_cycles);
                 infer_parallel_fork(&mut func, fork_join_map);
                 infer_vectorizable(&mut func, fork_join_map);
@@ -986,41 +1121,113 @@ fn run_pass(
         }
         Pass::Inline => {
             assert!(args.is_empty());
-            if let Some(_) = selection { 
-                return Err(SchedulerError::PassError { 
-                    pass: "inline".to_string(), 
-                    error: "must be applied to the entire module".to_string()
+            if let Some(_) = selection {
+                return Err(SchedulerError::PassError {
+                    pass: "inline".to_string(),
+                    error: "must be applied to the entire module (currently)".to_string(),
                 });
             }
 
             pm.make_callgraph();
             let callgraph = pm.callgraph.take().unwrap();
-            let mut editors = build_selection(pm, selection).into_iter().map(|f| f.unwrap()).collect::<Vec<_>>();
+            let mut editors = build_selection(pm, selection)
+                .into_iter()
+                .map(|f| f.unwrap())
+                .collect::<Vec<_>>();
             inline(&mut editors, &callgraph);
 
-            for func in editors { changed |= func.modified(); }
+            for func in editors {
+                changed |= func.modified();
+            }
 
             pm.delete_gravestones();
             pm.clear_analyses();
         }
         Pass::InterproceduralSROA => {
             assert!(args.is_empty());
-            if let Some(_) = selection { 
-                return Err(SchedulerError::PassError { 
-                    pass: "interproceduralSROA".to_string(), 
-                    error: "must be applied to the entire module".to_string()
+            if let Some(_) = selection {
+                return Err(SchedulerError::PassError {
+                    pass: "interproceduralSROA".to_string(),
+                    error: "must be applied to the entire module".to_string(),
                 });
             }
 
-            let mut editors = build_selection(pm, selection).into_iter().map(|f| f.unwrap()).collect::<Vec<_>>();
+            let mut editors = build_selection(pm, selection)
+                .into_iter()
+                .map(|f| f.unwrap())
+                .collect::<Vec<_>>();
             interprocedural_sroa(&mut editors);
 
-            for func in editors { changed |= func.modified(); }
+            for func in editors {
+                changed |= func.modified();
+            }
 
             pm.delete_gravestones();
             pm.clear_analyses();
         }
-        _ => todo!(),
+        Pass::Outline => {
+            todo!("outlining is weird")
+        }
+        Pass::PhiElim => {
+            assert!(args.is_empty());
+            for func in build_selection(pm, selection) {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                phi_elim(&mut func);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::Predication => {
+            todo!("Predication doesn't use editor")
+        }
+        Pass::SROA => {
+            assert!(args.is_empty());
+            pm.make_reverse_postorders();
+            pm.make_typing();
+            let reverse_postorders = pm.reverse_postorders.take().unwrap();
+            let typing = pm.typing.take().unwrap();
+
+            for ((func, reverse_postorder), types) in build_selection(pm, selection)
+                .into_iter()
+                .zip(reverse_postorders.iter())
+                .zip(typing.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                sroa(&mut func, reverse_postorder, types);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::Unforkify => {
+            assert!(args.is_empty());
+            pm.make_fork_join_maps();
+            let fork_join_maps = pm.fork_join_maps.take().unwrap();
+
+            for (func, fork_join_map) in build_selection(pm, selection)
+                .into_iter()
+                .zip(fork_join_maps.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                unforkify(&mut func, fork_join_map);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
+        Pass::Verify => {
+            todo!("Verification not implemented")
+        }
+        Pass::Xdot => {
+            todo!("Xdot not implemented")
+        }
     }
 
     Ok((result, changed))
-- 
GitLab


From f34b1021911009cb5302bcf6ecce2fdce410f79d Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 11:39:24 -0600
Subject: [PATCH 28/46] Update codegen

---
 Cargo.lock                |  1 +
 juno_scheduler/Cargo.toml |  1 +
 juno_scheduler/src/pm.rs  | 12 ++++++------
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index e0fc9ef1..cf75e5dd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -791,6 +791,7 @@ dependencies = [
  "juno_utils",
  "lrlex",
  "lrpar",
+ "tempfile",
 ]
 
 [[package]]
diff --git a/juno_scheduler/Cargo.toml b/juno_scheduler/Cargo.toml
index 15b1db32..1c837d4a 100644
--- a/juno_scheduler/Cargo.toml
+++ b/juno_scheduler/Cargo.toml
@@ -13,6 +13,7 @@ lrpar = "0.13"
 cfgrammar = "0.13"
 lrlex = "0.13"
 lrpar = "0.13"
+tempfile = "*"
 hercules_cg = { path = "../hercules_cg" }
 hercules_ir = { path = "../hercules_ir" }
 hercules_opt = { path = "../hercules_opt" }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 0e603bf2..f40894be 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -9,6 +9,8 @@ use hercules_opt::{
     predication, sroa, unforkify,
 };
 
+use tempfile::TempDir;
+
 use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
 
@@ -427,7 +429,6 @@ impl PassManager {
     }
 
     fn codegen(mut self, output_dir: String, module_name: String) -> Result<(), SchedulerError> {
-        self.make_reverse_postorders();
         self.make_typing();
         self.make_control_subgraphs();
         self.make_collection_objects();
@@ -439,7 +440,6 @@ impl PassManager {
             constants,
             dynamic_constants,
             labels,
-            reverse_postorders: Some(reverse_postorders),
             typing: Some(typing),
             control_subgraphs: Some(control_subgraphs),
             bbs: Some(bbs),
@@ -504,12 +504,13 @@ impl PassManager {
         println!("{}", rust_rt);
 
         // Write the LLVM IR into a temporary file.
-        let mut tmp_path = temp_dir();
+        let tmp_dir = TempDir::new().unwrap();
+        let mut tmp_path = tmp_dir.path().to_path_buf();
         tmp_path.push(format!("{}.ll", module_name));
+        println!("{}", tmp_path.display());
         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);
@@ -525,15 +526,14 @@ impl PassManager {
             .spawn()
             .expect("Error running clang. Is it installed?");
         assert!(clang_process.wait().unwrap().success());
-        println!("{}", output_archive);
 
         // Write the Rust runtime into a file.
         let output_rt = format!("{}/rt_{}.hrt", output_dir, module_name);
+        println!("{}", output_rt);
         let mut file =
             File::create(&output_rt).expect("PANIC: Unable to open output Rust runtime file.");
         file.write_all(rust_rt.as_bytes())
             .expect("PANIC: Unable to write output Rust runtime file contents.");
-        println!("{}", output_rt);
 
         Ok(())
     }
-- 
GitLab


From f36edcf8baf0466ebef71cd5d91c70572753f49c Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 11:58:02 -0600
Subject: [PATCH 29/46] Selection analyses we need

---
 juno_scheduler/src/pm.rs | 152 +++++++++++++++++++++++----------------
 1 file changed, 92 insertions(+), 60 deletions(-)

diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index f40894be..2da8def2 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -15,7 +15,7 @@ use juno_utils::env::Env;
 use juno_utils::stringtab::StringTable;
 
 use std::cell::RefCell;
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::env::temp_dir;
 use std::fmt;
 use std::fs::File;
@@ -835,6 +835,68 @@ impl FunctionSelection {
     }
 }
 
+fn build_editors<'a>(pm: &'a mut PassManager) -> Vec<FunctionEditor<'a>> {
+    pm.make_def_uses();
+    let def_uses = pm.def_uses.take().unwrap();
+    pm.functions
+        .iter_mut()
+        .zip(def_uses.iter())
+        .enumerate()
+        .map(|(idx, (func, def_use))| {
+            FunctionEditor::new(
+                func,
+                FunctionID::new(idx),
+                &pm.constants,
+                &pm.dynamic_constants,
+                &pm.types,
+                &pm.labels,
+                def_use,
+            )
+        })
+        .collect()
+}
+
+// With a selection, we process it to identify which labels in which functions are to be selected
+fn construct_selection(pm: &PassManager, selection: Vec<CodeLocation>) -> Vec<FunctionSelection> {
+    let mut selected = vec![FunctionSelection::Nothing(); pm.functions.len()];
+    for loc in selection {
+        match loc {
+            CodeLocation::Label(label) => selected[label.func.idx()].add_label(label.label),
+            CodeLocation::Function(func) => selected[func.idx()].add_everything(),
+        }
+    }
+    selected
+}
+
+// Given a selection, constructs the set (for each function) of the nodes selected
+fn selection_as_set(
+    pm: &PassManager,
+    selection: Option<Vec<CodeLocation>>,
+) -> Vec<BTreeSet<NodeID>> {
+    if let Some(selection) = selection {
+        let selection = construct_selection(pm, selection);
+        selection
+            .into_iter()
+            .zip(pm.functions.iter())
+            .map(|(selected, func)| match selected {
+                FunctionSelection::Nothing() => BTreeSet::new(),
+                FunctionSelection::Everything() => {
+                    (0..func.nodes.len()).map(|idx| NodeID::new(idx)).collect()
+                }
+                FunctionSelection::Labels(labels) => (0..func.nodes.len())
+                    .filter(|idx| !func.labels[*idx].is_disjoint(&labels))
+                    .map(|idx| NodeID::new(idx))
+                    .collect(),
+            })
+            .collect()
+    } else {
+        pm.functions
+            .iter()
+            .map(|func| (0..func.nodes.len()).map(|idx| NodeID::new(idx)).collect())
+            .collect()
+    }
+}
+
 fn build_selection<'a>(
     pm: &'a mut PassManager,
     selection: Option<Vec<CodeLocation>>,
@@ -843,73 +905,43 @@ fn build_selection<'a>(
     pm.make_def_uses();
     let def_uses = pm.def_uses.take().unwrap();
 
-    // The results are a list of (optional) function editors, which edit the function at that index
-    let mut res = vec![];
-
     if let Some(selection) = selection {
-        // With a selection, we first process it to identify which labels in which functions are to
-        // be selected. Then, we use that to construct the editors
-        let mut selected = vec![FunctionSelection::Nothing(); pm.functions.len()];
-
-        for loc in selection {
-            match loc {
-                CodeLocation::Label(label) => selected[label.func.idx()].add_label(label.label),
-                CodeLocation::Function(func) => selected[func.idx()].add_everything(),
-            }
-        }
+        let selected = construct_selection(pm, selection);
 
-        for (idx, ((func, selected), def_use)) in pm
-            .functions
+        pm.functions
             .iter_mut()
             .zip(selected.iter())
             .zip(def_uses.iter())
             .enumerate()
-        {
-            match selected {
-                FunctionSelection::Nothing() => {
-                    res.push(None);
-                }
-                FunctionSelection::Everything() => {
-                    res.push(Some(FunctionEditor::new(
-                        func,
-                        FunctionID::new(idx),
-                        &pm.constants,
-                        &pm.dynamic_constants,
-                        &pm.types,
-                        &pm.labels,
-                        def_use,
-                    )));
-                }
-                FunctionSelection::Labels(labels) => {
-                    res.push(Some(FunctionEditor::new_labeled(
-                        func,
-                        FunctionID::new(idx),
-                        &pm.constants,
-                        &pm.dynamic_constants,
-                        &pm.types,
-                        &pm.labels,
-                        def_use,
-                        labels,
-                    )));
-                }
-            }
-        }
+            .map(|(idx, ((func, selected), def_use))| match selected {
+                FunctionSelection::Nothing() => None,
+                FunctionSelection::Everything() => Some(FunctionEditor::new(
+                    func,
+                    FunctionID::new(idx),
+                    &pm.constants,
+                    &pm.dynamic_constants,
+                    &pm.types,
+                    &pm.labels,
+                    def_use,
+                )),
+                FunctionSelection::Labels(labels) => Some(FunctionEditor::new_labeled(
+                    func,
+                    FunctionID::new(idx),
+                    &pm.constants,
+                    &pm.dynamic_constants,
+                    &pm.types,
+                    &pm.labels,
+                    def_use,
+                    labels,
+                )),
+            })
+            .collect()
     } else {
-        // If no selection is provided, we apply the optimization to everything
-        for (idx, (func, def_use)) in pm.functions.iter_mut().zip(def_uses.iter()).enumerate() {
-            res.push(Some(FunctionEditor::new(
-                func,
-                FunctionID::new(idx),
-                &pm.constants,
-                &pm.dynamic_constants,
-                &pm.types,
-                &pm.labels,
-                def_use,
-            )));
-        }
+        build_editors(pm)
+            .into_iter()
+            .map(|func| Some(func))
+            .collect()
     }
-
-    res
 }
 
 fn run_pass(
-- 
GitLab


From 571f41f0506210c5a1f7ad0f0ee7490355d8f640 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 14:09:42 -0600
Subject: [PATCH 30/46] Add outlining and auto-outline

---
 juno_scheduler/src/compile.rs |   1 +
 juno_scheduler/src/default.rs |   2 +-
 juno_scheduler/src/ir.rs      |   1 +
 juno_scheduler/src/pm.rs      | 241 ++++++++++++++++++++++++++++------
 4 files changed, 206 insertions(+), 39 deletions(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 252a959f..6006abe0 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -92,6 +92,7 @@ impl FromStr for Appliable {
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
+            "auto-outline" => Ok(Appliable::Pass(ir::Pass::AutoOutline)),
             "ccp" => Ok(Appliable::Pass(ir::Pass::CCP)),
             "dce" => Ok(Appliable::Pass(ir::Pass::DCE)),
             "delete-uncalled" => Ok(Appliable::Pass(ir::Pass::DeleteUncalled)),
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index ffa3c0b3..793f1799 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -50,7 +50,7 @@ pub fn default_schedule() -> ScheduleStmt {
         GVN,
         DCE,
         DCE,
-        Outline,
+        AutoOutline,
         InterproceduralSROA,
         SROA,
         InferSchedules,
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index acbd3ac4..922f6158 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -4,6 +4,7 @@ use self::hercules_ir::ir::{Device, Schedule};
 
 #[derive(Debug, Copy, Clone)]
 pub enum Pass {
+    AutoOutline,
     CCP,
     DCE,
     DeleteUncalled,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 2da8def2..6ce8621d 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -4,9 +4,10 @@ use hercules_cg::*;
 use hercules_ir::*;
 use hercules_opt::FunctionEditor;
 use hercules_opt::{
-    ccp, dce, float_collections, fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce,
-    infer_tight_associative, infer_vectorizable, inline, interprocedural_sroa, phi_elim,
-    predication, sroa, unforkify,
+    ccp, collapse_returns, dce, dumb_outline, ensure_between_control_flow, float_collections,
+    fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce, infer_tight_associative,
+    infer_vectorizable, inline, interprocedural_sroa, outline, phi_elim, predication, sroa,
+    unforkify,
 };
 
 use tempfile::TempDir;
@@ -868,32 +869,50 @@ fn construct_selection(pm: &PassManager, selection: Vec<CodeLocation>) -> Vec<Fu
     selected
 }
 
-// Given a selection, constructs the set (for each function) of the nodes selected
+// Given a selection, constructs the set of the nodes selected for a single function, returning the
+// function's id
 fn selection_as_set(
     pm: &PassManager,
     selection: Option<Vec<CodeLocation>>,
-) -> Vec<BTreeSet<NodeID>> {
+) -> Option<(BTreeSet<NodeID>, FunctionID)> {
     if let Some(selection) = selection {
         let selection = construct_selection(pm, selection);
-        selection
-            .into_iter()
-            .zip(pm.functions.iter())
-            .map(|(selected, func)| match selected {
-                FunctionSelection::Nothing() => BTreeSet::new(),
-                FunctionSelection::Everything() => {
-                    (0..func.nodes.len()).map(|idx| NodeID::new(idx)).collect()
-                }
-                FunctionSelection::Labels(labels) => (0..func.nodes.len())
-                    .filter(|idx| !func.labels[*idx].is_disjoint(&labels))
-                    .map(|idx| NodeID::new(idx))
-                    .collect(),
-            })
-            .collect()
+        let mut result = None;
+
+        for (idx, (selected, func)) in selection.into_iter().zip(pm.functions.iter()).enumerate() {
+            match selected {
+                FunctionSelection::Nothing() => {}
+                FunctionSelection::Everything() => match result {
+                    Some(_) => {
+                        return None;
+                    }
+                    None => {
+                        result = Some((
+                            (0..func.nodes.len()).map(|i| NodeID::new(i)).collect(),
+                            FunctionID::new(idx),
+                        ));
+                    }
+                },
+                FunctionSelection::Labels(labels) => match result {
+                    Some(_) => {
+                        return None;
+                    }
+                    None => {
+                        result = Some((
+                            (0..func.nodes.len())
+                                .filter(|i| !func.labels[*i].is_disjoint(&labels))
+                                .map(|i| NodeID::new(i))
+                                .collect(),
+                            FunctionID::new(idx),
+                        ));
+                    }
+                },
+            }
+        }
+
+        result
     } else {
-        pm.functions
-            .iter()
-            .map(|func| (0..func.nodes.len()).map(|idx| NodeID::new(idx)).collect())
-            .collect()
+        None
     }
 }
 
@@ -956,6 +975,96 @@ fn run_pass(
     let mut changed = false;
 
     match pass {
+        Pass::AutoOutline => {
+            if let Some(_) = selection {
+                return Err(SchedulerError::PassError {
+                    pass: "autoOutline".to_string(),
+                    error: "must be applied to the entire module".to_string(),
+                });
+            }
+
+            pm.make_def_uses();
+            let def_uses = pm.def_uses.take().unwrap();
+            let mut editors: Vec<_> = pm
+                .functions
+                .iter_mut()
+                .zip(def_uses.iter())
+                .enumerate()
+                .map(|(idx, (func, def_use))| {
+                    FunctionEditor::new(
+                        func,
+                        FunctionID::new(idx),
+                        &pm.constants,
+                        &pm.dynamic_constants,
+                        &pm.types,
+                        &pm.labels,
+                        def_use,
+                    )
+                })
+                .collect();
+            for editor in editors.iter_mut() {
+                collapse_returns(editor);
+                ensure_between_control_flow(editor);
+            }
+            pm.clear_analyses();
+
+            pm.make_def_uses();
+            pm.make_typing();
+            pm.make_control_subgraphs();
+            pm.make_doms();
+
+            let def_uses = pm.def_uses.take().unwrap();
+            let typing = pm.typing.take().unwrap();
+            let control_subgraphs = pm.control_subgraphs.take().unwrap();
+            let doms = pm.doms.take().unwrap();
+            let old_num_funcs = pm.functions.len();
+
+            let mut editors: Vec<_> = pm
+                .functions
+                .iter_mut()
+                .zip(def_uses.iter())
+                .enumerate()
+                .map(|(idx, (func, def_use))| {
+                    FunctionEditor::new(
+                        func,
+                        FunctionID::new(idx),
+                        &pm.constants,
+                        &pm.dynamic_constants,
+                        &pm.types,
+                        &pm.labels,
+                        def_use,
+                    )
+                })
+                .collect();
+
+            let mut new_funcs = vec![];
+            for (idx, editor) in editors.iter_mut().enumerate() {
+                let new_func_id = FunctionID::new(old_num_funcs + new_funcs.len());
+                let new_func = dumb_outline(
+                    editor,
+                    &typing[idx],
+                    &control_subgraphs[idx],
+                    &doms[idx],
+                    new_func_id,
+                );
+                if let Some(new_func) = new_func {
+                    let Value::Record { ref mut fields } = result else {
+                        panic!("AutoOutline produces a record");
+                    };
+                    fields.insert(
+                        new_func.name.clone(),
+                        Value::HerculesFunction { func: new_func_id },
+                    );
+                    new_funcs.push(new_func);
+                }
+            }
+
+            for func in pm.functions.iter_mut() {
+                func.delete_gravestones();
+            }
+            pm.functions.extend(new_funcs);
+            pm.clear_analyses();
+        }
         Pass::CCP => {
             assert!(args.is_empty());
             pm.make_reverse_postorders();
@@ -1004,10 +1113,7 @@ fn run_pass(
 
             let devices = device_placement(&pm.functions, &callgraph);
 
-            let mut editors = build_selection(pm, selection)
-                .into_iter()
-                .map(|f| f.unwrap())
-                .collect::<Vec<_>>();
+            let mut editors = build_editors(pm);
             float_collections(&mut editors, &typing, &callgraph, &devices);
 
             for func in editors {
@@ -1082,9 +1188,8 @@ fn run_pass(
                         fork_join_map,
                     ),
                     loops,
-                ) in build_selection(pm, selection.clone())
+                ) in build_editors(pm)
                     .into_iter()
-                    .map(|f| f.unwrap())
                     .zip(def_uses.iter())
                     .zip(reverse_postorders.iter())
                     .zip(typing.iter())
@@ -1162,10 +1267,8 @@ fn run_pass(
 
             pm.make_callgraph();
             let callgraph = pm.callgraph.take().unwrap();
-            let mut editors = build_selection(pm, selection)
-                .into_iter()
-                .map(|f| f.unwrap())
-                .collect::<Vec<_>>();
+
+            let mut editors = build_editors(pm);
             inline(&mut editors, &callgraph);
 
             for func in editors {
@@ -1184,10 +1287,7 @@ fn run_pass(
                 });
             }
 
-            let mut editors = build_selection(pm, selection)
-                .into_iter()
-                .map(|f| f.unwrap())
-                .collect::<Vec<_>>();
+            let mut editors = build_editors(pm);
             interprocedural_sroa(&mut editors);
 
             for func in editors {
@@ -1198,7 +1298,72 @@ fn run_pass(
             pm.clear_analyses();
         }
         Pass::Outline => {
-            todo!("outlining is weird")
+            let Some((nodes, func)) = selection_as_set(pm, selection) else {
+                return Err(SchedulerError::PassError {
+                    pass: "outline".to_string(),
+                    error: "must be applied to nodes in a single function".to_string(),
+                });
+            };
+
+            pm.make_def_uses();
+            let def_uses = pm.def_uses.take().unwrap();
+
+            let mut editor = FunctionEditor::new(
+                &mut pm.functions[func.idx()],
+                func,
+                &pm.constants,
+                &pm.dynamic_constants,
+                &pm.types,
+                &pm.labels,
+                &def_uses[func.idx()],
+            );
+
+            collapse_returns(&mut editor);
+            ensure_between_control_flow(&mut editor);
+            pm.clear_analyses();
+
+            pm.make_def_uses();
+            pm.make_typing();
+            pm.make_control_subgraphs();
+            pm.make_doms();
+
+            let def_uses = pm.def_uses.take().unwrap();
+            let typing = pm.typing.take().unwrap();
+            let control_subgraphs = pm.control_subgraphs.take().unwrap();
+            let doms = pm.doms.take().unwrap();
+            let new_func_id = FunctionID::new(pm.functions.len());
+
+            let mut editor = FunctionEditor::new(
+                &mut pm.functions[func.idx()],
+                func,
+                &pm.constants,
+                &pm.dynamic_constants,
+                &pm.types,
+                &pm.labels,
+                &def_uses[func.idx()],
+            );
+
+            let new_func = outline(
+                &mut editor,
+                &typing[func.idx()],
+                &control_subgraphs[func.idx()],
+                &doms[func.idx()],
+                &nodes,
+                new_func_id,
+            );
+            let Some(new_func) = new_func else {
+                return Err(SchedulerError::PassError {
+                    pass: "outlining".to_string(),
+                    error: "failed to outline".to_string(),
+                });
+            };
+
+            pm.functions.push(new_func);
+            changed = true;
+            pm.functions[func.idx()].delete_gravestones();
+            pm.clear_analyses();
+
+            result = Value::HerculesFunction { func: new_func_id };
         }
         Pass::PhiElim => {
             assert!(args.is_empty());
-- 
GitLab


From d7b8e3c9a80d3cfee3786f804cc8afec73a9ead8 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 16:54:19 -0600
Subject: [PATCH 31/46] Support for xdot and verify

---
 juno_scheduler/src/pm.rs | 77 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 75 insertions(+), 2 deletions(-)

diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 6ce8621d..046e3597 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -429,6 +429,36 @@ impl PassManager {
         self.callgraph = None;
     }
 
+    fn with_mod<B, F>(&mut self, mut f: F) -> B
+    where
+        F: FnMut(&mut Module) -> B,
+    {
+        let mut module = Module {
+            functions: std::mem::take(&mut self.functions),
+            types: self.types.take(),
+            constants: self.constants.take(),
+            dynamic_constants: self.dynamic_constants.take(),
+            labels: self.labels.take(),
+        };
+
+        let res = f(&mut module);
+
+        let Module {
+            functions,
+            types,
+            constants,
+            dynamic_constants,
+            labels,
+        } = module;
+        self.functions = functions;
+        self.types.replace(types);
+        self.constants.replace(constants);
+        self.dynamic_constants.replace(dynamic_constants);
+        self.labels.replace(labels);
+
+        res
+    }
+
     fn codegen(mut self, output_dir: String, module_name: String) -> Result<(), SchedulerError> {
         self.make_typing();
         self.make_control_subgraphs();
@@ -1420,10 +1450,53 @@ fn run_pass(
             pm.clear_analyses();
         }
         Pass::Verify => {
-            todo!("Verification not implemented")
+            assert!(args.is_empty());
+            let (def_uses, reverse_postorders, typing, subgraphs, doms, postdoms, fork_join_maps) =
+                pm.with_mod(|module| verify(module))
+                    .map_err(|msg| SchedulerError::PassError {
+                        pass: "verify".to_string(),
+                        error: format!("failed: {}", msg),
+                    })?;
+
+            // Verification produces a bunch of analysis results that
+            // may be useful for later passes.
+            pm.def_uses = Some(def_uses);
+            pm.reverse_postorders = Some(reverse_postorders);
+            pm.typing = Some(typing);
+            pm.control_subgraphs = Some(subgraphs);
+            pm.doms = Some(doms);
+            pm.postdoms = Some(postdoms);
+            pm.fork_join_maps = Some(fork_join_maps);
         }
         Pass::Xdot => {
-            todo!("Xdot not implemented")
+            assert!(args.len() == 1);
+            let force_analyses = match args[0] {
+                Value::Boolean { val } => val,
+                _ => {
+                    return Err(SchedulerError::PassError {
+                        pass: "xdot".to_string(),
+                        error: "expected boolean argument".to_string(),
+                    });
+                }
+            };
+
+            pm.make_reverse_postorders();
+            if force_analyses {
+                pm.make_doms();
+                pm.make_fork_join_maps();
+            }
+
+            let reverse_postorders = pm.reverse_postorders.take().unwrap();
+            let doms = pm.doms.take();
+            let fork_join_maps = pm.fork_join_maps.take();
+            pm.with_mod(|module| {
+                xdot_module(
+                    module,
+                    &reverse_postorders,
+                    doms.as_ref(),
+                    fork_join_maps.as_ref(),
+                )
+            });
         }
     }
 
-- 
GitLab


From 9768157390801ecb7cc339035009ed55e9dfbb34 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 13 Jan 2025 17:19:57 -0600
Subject: [PATCH 32/46] Testing and fixing some issues

---
 Cargo.lock                               | 11 +++++++++
 Cargo.toml                               |  1 +
 juno_frontend/src/codegen.rs             |  1 +
 juno_frontend/src/semant.rs              |  2 +-
 juno_frontend/src/ssa.rs                 |  2 +-
 juno_samples/schedule_test/Cargo.toml    | 19 +++++++++++++++
 juno_samples/schedule_test/build.rs      | 11 +++++++++
 juno_samples/schedule_test/src/code.jn   | 30 ++++++++++++++++++++++++
 juno_samples/schedule_test/src/main.rs   |  9 +++++++
 juno_samples/schedule_test/src/sched.sch |  2 ++
 juno_scheduler/src/compile.rs            |  2 ++
 11 files changed, 88 insertions(+), 2 deletions(-)
 create mode 100644 juno_samples/schedule_test/Cargo.toml
 create mode 100644 juno_samples/schedule_test/build.rs
 create mode 100644 juno_samples/schedule_test/src/code.jn
 create mode 100644 juno_samples/schedule_test/src/main.rs
 create mode 100644 juno_samples/schedule_test/src/sched.sch

diff --git a/Cargo.lock b/Cargo.lock
index cf75e5dd..22451f12 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -780,6 +780,17 @@ dependencies = [
  "with_builtin_macros",
 ]
 
+[[package]]
+name = "juno_schedule_test"
+version = "0.1.0"
+dependencies = [
+ "async-std",
+ "hercules_rt",
+ "juno_build",
+ "rand",
+ "with_builtin_macros",
+]
+
 [[package]]
 name = "juno_scheduler"
 version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index d0537c95..e3fa28fc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,4 +28,5 @@ members = [
 	"juno_samples/nested_ccp",
 	"juno_samples/antideps",
 	"juno_samples/implicit_clone",
+  "juno_samples/schedule_test",
 ]
diff --git a/juno_frontend/src/codegen.rs b/juno_frontend/src/codegen.rs
index 0a52a88e..2ff9fa9f 100644
--- a/juno_frontend/src/codegen.rs
+++ b/juno_frontend/src/codegen.rs
@@ -132,6 +132,7 @@ impl CodeGenerator<'_> {
                     )
                     .unwrap();
 
+                self.juno_info.func_info.func_ids[func_idx].push(func_id);
                 self.functions.insert((func_idx, ty_args), func_id);
                 self.worklist
                     .push_back((func_idx, solver_inst, func_id, entry));
diff --git a/juno_frontend/src/semant.rs b/juno_frontend/src/semant.rs
index 3a8c0ccb..2fe4bf88 100644
--- a/juno_frontend/src/semant.rs
+++ b/juno_frontend/src/semant.rs
@@ -2407,7 +2407,7 @@ fn process_stmt(
             label,
             stmt,
         } => {
-            let label_str = lexer.span_str(label).to_string();
+            let label_str = lexer.span_str(label)[1..].to_string();
             let label_id = labels.lookup_string(label_str);
 
             let (body, reach_end) = process_stmt(
diff --git a/juno_frontend/src/ssa.rs b/juno_frontend/src/ssa.rs
index 578f7a9a..7076d622 100644
--- a/juno_frontend/src/ssa.rs
+++ b/juno_frontend/src/ssa.rs
@@ -7,9 +7,9 @@
 
 use std::collections::{HashMap, HashSet};
 
+use crate::labeled_builder::LabeledBuilder;
 use hercules_ir::build::*;
 use hercules_ir::ir::*;
-use crate::labeled_builder::LabeledBuilder;
 
 pub struct SSA {
     // Map from variable (usize) to build (NodeID) to definition (NodeID)
diff --git a/juno_samples/schedule_test/Cargo.toml b/juno_samples/schedule_test/Cargo.toml
new file mode 100644
index 00000000..be5d949b
--- /dev/null
+++ b/juno_samples/schedule_test/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "juno_schedule_test"
+version = "0.1.0"
+authors = ["Aaron Councilman <aaronjc4@illinois.edu>"]
+edition = "2021"
+
+[[bin]]
+name = "juno_schedule_test"
+path = "src/main.rs"
+
+[build-dependencies]
+juno_build = { path = "../../juno_build" }
+
+[dependencies]
+juno_build = { path = "../../juno_build" }
+hercules_rt = { path = "../../hercules_rt" }
+with_builtin_macros = "0.1.0"
+async-std = "*"
+rand = "*"
diff --git a/juno_samples/schedule_test/build.rs b/juno_samples/schedule_test/build.rs
new file mode 100644
index 00000000..4a428247
--- /dev/null
+++ b/juno_samples/schedule_test/build.rs
@@ -0,0 +1,11 @@
+use juno_build::JunoCompiler;
+
+fn main() {
+    JunoCompiler::new()
+        .file_in_src("code.jn")
+        .unwrap()
+        .schedule_in_src("sched.sch")
+        .unwrap()
+        .build()
+        .unwrap();
+}
diff --git a/juno_samples/schedule_test/src/code.jn b/juno_samples/schedule_test/src/code.jn
new file mode 100644
index 00000000..9e801af5
--- /dev/null
+++ b/juno_samples/schedule_test/src/code.jn
@@ -0,0 +1,30 @@
+#[entry]
+fn test<n, m, k: usize>(a: i32[n, m], b: i32[m, k], c: i32[k]) -> i32[n] {
+  let prod: i32[n, k];
+
+  @outer for i = 0 to n {
+    @middle for j = 0 to k {
+      let val = 0;
+
+      @inner for k = 0 to m {
+        val += a[i, k] * b[i, j];
+      }
+
+      prod[i, j] = val;
+    }
+  }
+
+  let res: i32[n];
+
+  @row for i = 0 to n {
+    let val = 0;
+
+    @col for j = 0 to k {
+      val += prod[i, j] * c[j];
+    }
+
+    res[i] = val;
+  }
+
+  return res;
+}
diff --git a/juno_samples/schedule_test/src/main.rs b/juno_samples/schedule_test/src/main.rs
new file mode 100644
index 00000000..4375d1b8
--- /dev/null
+++ b/juno_samples/schedule_test/src/main.rs
@@ -0,0 +1,9 @@
+#![feature(box_as_ptr, let_chains)]
+
+use hercules_rt::HerculesBox;
+
+juno_build::juno!("code");
+
+fn main() {
+    todo!()
+}
diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
new file mode 100644
index 00000000..865d6e71
--- /dev/null
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -0,0 +1,2 @@
+outline(test@outer);
+xdot[true](*);
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 6006abe0..ac3f86f6 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -468,6 +468,8 @@ fn compile_macro_def(
     macrostab: &mut StringTable,
     macros: &mut Env<usize, MacroInfo>,
 ) -> Result<MacroInfo, ScheduleCompilerError> {
+    // FIXME: The body should be checked in an environment that prohibits running anything on
+    // everything (*) and check that only local variables/parameters are used
     Ok(MacroInfo {
         params,
         selection_name,
-- 
GitLab


From 39e58dc3fc8c26bd19fac578f35c3c7bd82d2f72 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 15 Jan 2025 09:29:41 -0600
Subject: [PATCH 33/46] Fixes to macros

---
 juno_samples/schedule_test/src/sched.sch | 19 +++++++++++++++++++
 juno_scheduler/src/compile.rs            |  9 +++++++--
 juno_scheduler/src/lang.l                |  6 +++---
 juno_scheduler/src/lang.y                |  9 +++++++--
 4 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index 865d6e71..22a63637 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -1,2 +1,21 @@
+macro juno-setup!(X) {
+  gvn(X);
+  phi-elim(X);
+  dce(X);
+}
+macro codegen-prep!(X) {
+  infer-schedules(X);
+  dce(X);
+  gcm(X);
+  dce(X);
+  float-collections(X);
+  gcm(X);
+}
+
+juno-setup!(*);
+
+xdot[true](*);
 outline(test@outer);
 xdot[true](*);
+
+codegen-prep!(*);
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 83a9a34d..bef7c63f 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -59,6 +59,8 @@ pub fn compile_schedule(
     let mut macrostab = StringTable::new();
     let mut macros = Env::new();
 
+    macros.open_scope();
+
     Ok(ir::ScheduleStmt::Block {
         body: compile_ops_as_block(sched, lexer, &mut macrostab, &mut macros)?,
     })
@@ -346,7 +348,7 @@ fn compile_expr(
             // evaluate all of the arguments and store them into new variables, using names that
             // cannot conflict with other values in the program and then we assign those variables
             // to the macro's parameters; this avoids any shadowing issues, for instance:
-            // macro!(3, x) where macro!'s arguments are named x and y becomes
+            // macro![3, x] where macro!'s arguments are named x and y becomes
             //      let #0 = 3; let #1 = x; let x = #0; let y = #1;
             // which has the desired semantics, as opposed to
             //      let x = 3; let y = x;
@@ -373,8 +375,11 @@ fn compile_expr(
                 },
             });
 
+            // Combine the evaluation and initialization code
+            arg_eval.extend(arg_setters);
+
             Ok(ExprResult::Expr(ir::ScheduleExp::Block {
-                body: arg_setters,
+                body: arg_eval,
                 res: Box::new(def),
             }))
         }
diff --git a/juno_scheduler/src/lang.l b/juno_scheduler/src/lang.l
index 8c2c1847..9d4c34bf 100644
--- a/juno_scheduler/src/lang.l
+++ b/juno_scheduler/src/lang.l
@@ -43,8 +43,8 @@ panic[\t \n\r]+after "panic_after"
 print[\t \n\r]+iter  "print_iter"
 stop[\t \n\r]+after  "stop_after"
 
-[a-zA-Z][a-zA-Z0-9_]*! "MACRO"
-[a-zA-Z][a-zA-Z0-9_]*  "ID"
-[0-9]+                 "INT"
+[a-zA-Z][a-zA-Z0-9_\-]*! "MACRO"
+[a-zA-Z][a-zA-Z0-9_\-]*  "ID"
+[0-9]+                   "INT"
 
 .                     "UNMATCHED"
diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y
index 597747c4..9cb72842 100644
--- a/juno_scheduler/src/lang.y
+++ b/juno_scheduler/src/lang.y
@@ -96,8 +96,13 @@ MacroDecl -> MacroDecl
 
 Params -> Vec<Span>
   :                  { vec![] }
-  | 'ID'             { vec![span_of_tok($1)] }
-  | 'ID' ',' Params  { snoc(span_of_tok($1), $3) }
+  | '[' Ids ']'      { $2 }
+  ;
+
+Ids -> Vec<Span>
+  :               { vec![] }
+  | 'ID'          { vec![span_of_tok($1)] }
+  | 'ID' ',' Ids  { snoc(span_of_tok($1), $3) }
   ;
 
 MacroDef -> OperationList : '{' Schedule '}' { $2 };
-- 
GitLab


From 09cce0435a9ac3fd9892f6c566bb1180231ee300 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 15 Jan 2025 09:38:40 -0600
Subject: [PATCH 34/46] Support predication

---
 Cargo.lock               | 11 -----------
 Cargo.toml               |  2 +-
 juno_scheduler/src/ir.rs |  1 +
 juno_scheduler/src/pm.rs | 31 +++++++++++++++++++++++++++++--
 4 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 22451f12..cf75e5dd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -780,17 +780,6 @@ dependencies = [
  "with_builtin_macros",
 ]
 
-[[package]]
-name = "juno_schedule_test"
-version = "0.1.0"
-dependencies = [
- "async-std",
- "hercules_rt",
- "juno_build",
- "rand",
- "with_builtin_macros",
-]
-
 [[package]]
 name = "juno_scheduler"
 version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index e3fa28fc..28c6af2d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,5 +28,5 @@ members = [
 	"juno_samples/nested_ccp",
 	"juno_samples/antideps",
 	"juno_samples/implicit_clone",
-  "juno_samples/schedule_test",
+  #"juno_samples/schedule_test",
 ]
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index 922f6158..ee50e010 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -22,6 +22,7 @@ pub enum Pass {
     Predication,
     SROA,
     Unforkify,
+    WritePredication,
     Verify,
     Xdot,
 }
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 046e3597..5fa57a8d 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -7,7 +7,7 @@ use hercules_opt::{
     ccp, collapse_returns, dce, dumb_outline, ensure_between_control_flow, float_collections,
     fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce, infer_tight_associative,
     infer_vectorizable, inline, interprocedural_sroa, outline, phi_elim, predication, sroa,
-    unforkify,
+    unforkify, write_predication,
 };
 
 use tempfile::TempDir;
@@ -1408,7 +1408,22 @@ fn run_pass(
             pm.clear_analyses();
         }
         Pass::Predication => {
-            todo!("Predication doesn't use editor")
+            assert!(args.is_empty());
+            pm.make_typing();
+            let typing = pm.typing.take().unwrap();
+
+            for (func, types) in build_selection(pm, selection)
+                .into_iter()
+                .zip(typing.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                predication(&mut func, types);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
         }
         Pass::SROA => {
             assert!(args.is_empty());
@@ -1449,6 +1464,18 @@ fn run_pass(
             pm.delete_gravestones();
             pm.clear_analyses();
         }
+        Pass::WritePredication => {
+            assert!(args.is_empty());
+            for func in build_selection(pm, selection) {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                write_predication(&mut func);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
         Pass::Verify => {
             assert!(args.is_empty());
             let (def_uses, reverse_postorders, typing, subgraphs, doms, postdoms, fork_join_maps) =
-- 
GitLab


From e2f1e3262bee64cb2eae029f220715f61be8e6d2 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 15 Jan 2025 11:08:03 -0600
Subject: [PATCH 35/46] Working on scheduler example

---
 Cargo.lock                               | 11 +++++++++++
 Cargo.toml                               |  2 +-
 juno_samples/schedule_test/src/sched.sch |  9 +++++++--
 3 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index cf75e5dd..22451f12 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -780,6 +780,17 @@ dependencies = [
  "with_builtin_macros",
 ]
 
+[[package]]
+name = "juno_schedule_test"
+version = "0.1.0"
+dependencies = [
+ "async-std",
+ "hercules_rt",
+ "juno_build",
+ "rand",
+ "with_builtin_macros",
+]
+
 [[package]]
 name = "juno_scheduler"
 version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index 28c6af2d..e3fa28fc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,5 +28,5 @@ members = [
 	"juno_samples/nested_ccp",
 	"juno_samples/antideps",
 	"juno_samples/implicit_clone",
-  #"juno_samples/schedule_test",
+  "juno_samples/schedule_test",
 ]
diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index 22a63637..a4e1d8a8 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -8,14 +8,19 @@ macro codegen-prep!(X) {
   dce(X);
   gcm(X);
   dce(X);
+  phi-elim(X);
   float-collections(X);
   gcm(X);
 }
 
 juno-setup!(*);
 
-xdot[true](*);
 outline(test@outer);
-xdot[true](*);
+outline(test@row);
+ip-sroa(*);
+sroa(*);
+//xdot[true](*);
+phi-elim(*);
 
 codegen-prep!(*);
+//xdot[true](*);
-- 
GitLab


From 05d8ed9ee1da168da89c94765349e292c798bb20 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 16 Jan 2025 15:07:50 -0600
Subject: [PATCH 36/46] Add store-load forwarding to scheduler

---
 juno_scheduler/src/compile.rs |  1 +
 juno_scheduler/src/default.rs |  2 +-
 juno_scheduler/src/ir.rs      |  1 +
 juno_scheduler/src/pm.rs      | 23 ++++++++++++++++++++++-
 4 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index bef7c63f..2c773212 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -112,6 +112,7 @@ impl FromStr for Appliable {
             "outline" => Ok(Appliable::Pass(ir::Pass::Outline)),
             "phi-elim" => Ok(Appliable::Pass(ir::Pass::PhiElim)),
             "predication" => Ok(Appliable::Pass(ir::Pass::Predication)),
+            "slf" | "store-load-forward" => Ok(Appliable::Pass(ir::Pass::SLF)),
             "sroa" => Ok(Appliable::Pass(ir::Pass::SROA)),
             "unforkify" => Ok(Appliable::Pass(ir::Pass::Unforkify)),
             "verify" => Ok(Appliable::Pass(ir::Pass::Verify)),
diff --git a/juno_scheduler/src/default.rs b/juno_scheduler/src/default.rs
index 15b34f43..f6a24d59 100644
--- a/juno_scheduler/src/default.rs
+++ b/juno_scheduler/src/default.rs
@@ -45,7 +45,7 @@ pub fn default_schedule() -> ScheduleStmt {
         WritePredication,
         PhiElim,
         DCE,
-        //SLF,
+        SLF,
         Predication,
         DCE,
         CCP,
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index ee50e010..2081b288 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -20,6 +20,7 @@ pub enum Pass {
     Outline,
     PhiElim,
     Predication,
+    SLF,
     SROA,
     Unforkify,
     WritePredication,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 5fa57a8d..15cb38b2 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -7,7 +7,7 @@ use hercules_opt::{
     ccp, collapse_returns, dce, dumb_outline, ensure_between_control_flow, float_collections,
     fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce, infer_tight_associative,
     infer_vectorizable, inline, interprocedural_sroa, outline, phi_elim, predication, sroa,
-    unforkify, write_predication,
+    unforkify, write_predication, slf,
 };
 
 use tempfile::TempDir;
@@ -1425,6 +1425,27 @@ fn run_pass(
             pm.delete_gravestones();
             pm.clear_analyses();
         }
+        Pass::SLF => {
+            assert!(args.is_empty());
+            pm.make_reverse_postorders();
+            pm.make_typing();
+            let reverse_postorders = pm.reverse_postorders.take().unwrap();
+            let typing = pm.typing.take().unwrap();
+
+            for ((func, reverse_postorder), types) in build_selection(pm, selection)
+                .into_iter()
+                .zip(reverse_postorders.iter())
+                .zip(typing.iter())
+            {
+                let Some(mut func) = func else {
+                    continue;
+                };
+                slf(&mut func, reverse_postorder, types);
+                changed |= func.modified();
+            }
+            pm.delete_gravestones();
+            pm.clear_analyses();
+        }
         Pass::SROA => {
             assert!(args.is_empty());
             pm.make_reverse_postorders();
-- 
GitLab


From f9e035a76424cea34f5b723b9bf312253fe5ba25 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Mon, 20 Jan 2025 10:24:08 -0600
Subject: [PATCH 37/46] Updated schedule

---
 Cargo.lock                               | 11 +++++++++++
 juno_samples/schedule_test/src/sched.sch |  1 -
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/Cargo.lock b/Cargo.lock
index 223ced4b..7dba2c76 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1088,6 +1088,17 @@ dependencies = [
  "with_builtin_macros",
 ]
 
+[[package]]
+name = "juno_schedule_test"
+version = "0.1.0"
+dependencies = [
+ "async-std",
+ "hercules_rt",
+ "juno_build",
+ "rand",
+ "with_builtin_macros",
+]
+
 [[package]]
 name = "juno_scheduler"
 version = "0.0.1"
diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index a4e1d8a8..ab482bdb 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -19,7 +19,6 @@ outline(test@outer);
 outline(test@row);
 ip-sroa(*);
 sroa(*);
-//xdot[true](*);
 phi-elim(*);
 
 codegen-prep!(*);
-- 
GitLab


From 8091e389beec9e7cd6a89ea860b0963a72cd18f6 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Tue, 21 Jan 2025 13:04:45 -0600
Subject: [PATCH 38/46] Working schedule example by avoiding GVN

---
 juno_samples/schedule_test/src/code.jn   |  2 +-
 juno_samples/schedule_test/src/main.rs   | 35 +++++++++++++++++++++++-
 juno_samples/schedule_test/src/sched.sch |  6 ++--
 3 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/juno_samples/schedule_test/src/code.jn b/juno_samples/schedule_test/src/code.jn
index 9e801af5..5bb923bf 100644
--- a/juno_samples/schedule_test/src/code.jn
+++ b/juno_samples/schedule_test/src/code.jn
@@ -7,7 +7,7 @@ fn test<n, m, k: usize>(a: i32[n, m], b: i32[m, k], c: i32[k]) -> i32[n] {
       let val = 0;
 
       @inner for k = 0 to m {
-        val += a[i, k] * b[i, j];
+        val += a[i, k] * b[k, j];
       }
 
       prod[i, j] = val;
diff --git a/juno_samples/schedule_test/src/main.rs b/juno_samples/schedule_test/src/main.rs
index 4375d1b8..a64cd16f 100644
--- a/juno_samples/schedule_test/src/main.rs
+++ b/juno_samples/schedule_test/src/main.rs
@@ -1,9 +1,42 @@
 #![feature(box_as_ptr, let_chains)]
 
+use rand::random;
+
 use hercules_rt::HerculesBox;
 
 juno_build::juno!("code");
 
 fn main() {
-    todo!()
+    async_std::task::block_on(async {
+        const N: usize = 256;
+        const M: usize = 64;
+        const K: usize = 128;
+        let a: Box<[i32]> = (0..N * M).map(|_| random::<i32>() % 100).collect();
+        let b: Box<[i32]> = (0..M * K).map(|_| random::<i32>() % 100).collect();
+        let c: Box<[i32]> = (0..K).map(|_| random::<i32>() % 100).collect();
+
+        let mut correct_res: Box<[i32]> = (0..N).map(|_| 0).collect();
+        for i in 0..N {
+            for j in 0..K {
+                let mut res = 0;
+                for k in 0..M {
+                    res += a[i * M + k] * b[k * K + j];
+                }
+                correct_res[i] += c[j] * res;
+            }
+        }
+
+        let mut res = {
+            let a = HerculesBox::from_slice(&a);
+            let b = HerculesBox::from_slice(&b);
+            let c = HerculesBox::from_slice(&c);
+            test(N as u64, M as u64, K as u64, a, b, c).await
+        };
+        assert_eq!(res.as_slice::<i32>(), &*correct_res);
+    });
+}
+
+#[test]
+fn schedule_test() {
+    main();
 }
diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index ab482bdb..ad2cc443 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -1,5 +1,5 @@
 macro juno-setup!(X) {
-  gvn(X);
+  //gvn(X);
   phi-elim(X);
   dce(X);
 }
@@ -13,10 +13,12 @@ macro codegen-prep!(X) {
   gcm(X);
 }
 
-juno-setup!(*);
 
+juno-setup!(*);
 outline(test@outer);
 outline(test@row);
+
+gvn(*);
 ip-sroa(*);
 sroa(*);
 phi-elim(*);
-- 
GitLab


From 70809c91fa5536fe9c5c88bf6f916c5e89053c9a Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 22 Jan 2025 14:00:04 -0600
Subject: [PATCH 39/46] Add schedules

---
 juno_scheduler/src/compile.rs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 2c773212..da1b3e94 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -122,7 +122,11 @@ impl FromStr for Appliable {
             "gpu" | "cuda" | "nvidia" => Ok(Appliable::Device(Device::CUDA)),
             "host" | "rust" | "rust-async" => Ok(Appliable::Device(Device::AsyncRust)),
 
-            // TODO: Schedules
+            "associative" => Ok(Appliable::Schedule(Schedule::TightAssociative)),
+            "parallel-fork" => Ok(Appliable::Schedule(Schedule::ParallelFork)),
+            "parallel-reduce" => Ok(Appliable::Schedule(Schedule::ParallelReduce)),
+            "vectorize" => Ok(Appliable::Schedule(Schedule::Vectorizable)),
+
             _ => Err(s.to_string()),
         }
     }
-- 
GitLab


From c589848acc6cf96e3411b50477e6c2a2d3e098ef Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 22 Jan 2025 14:06:08 -0600
Subject: [PATCH 40/46] Remove old pass manager and driver

---
 Cargo.lock                                 |   32 -
 Cargo.toml                                 |    2 -
 hercules_opt/src/lib.rs                    |    2 -
 hercules_opt/src/pass.rs                   | 1167 --------------------
 hercules_tools/hercules_driver/Cargo.toml  |   12 -
 hercules_tools/hercules_driver/src/main.rs |   58 -
 6 files changed, 1273 deletions(-)
 delete mode 100644 hercules_opt/src/pass.rs
 delete mode 100644 hercules_tools/hercules_driver/Cargo.toml
 delete mode 100644 hercules_tools/hercules_driver/src/main.rs

diff --git a/Cargo.lock b/Cargo.lock
index 7dba2c76..bfb6cb23 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -259,12 +259,6 @@ dependencies = [
  "arrayvec",
 ]
 
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
 [[package]]
 name = "bincode"
 version = "1.3.3"
@@ -291,9 +285,6 @@ name = "bitflags"
 version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
-dependencies = [
- "serde",
-]
 
 [[package]]
 name = "bitstream-io"
@@ -825,17 +816,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "hercules_driver"
-version = "0.1.0"
-dependencies = [
- "clap",
- "hercules_ir",
- "hercules_opt",
- "postcard",
- "ron",
-]
-
 [[package]]
 name = "hercules_ir"
 version = "0.1.0"
@@ -1767,18 +1747,6 @@ version = "0.8.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
 
-[[package]]
-name = "ron"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
-dependencies = [
- "base64",
- "bitflags 2.7.0",
- "serde",
- "serde_derive",
-]
-
 [[package]]
 name = "rustc_version"
 version = "0.4.1"
diff --git a/Cargo.toml b/Cargo.toml
index 03c989ad..6adefdba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,8 +6,6 @@ members = [
 	"hercules_opt",
 	"hercules_rt",
 	
-	"hercules_tools/hercules_driver",
-
 	"juno_utils",
 	"juno_frontend",
 	"juno_scheduler",
diff --git a/hercules_opt/src/lib.rs b/hercules_opt/src/lib.rs
index 9935703e..f1292e36 100644
--- a/hercules_opt/src/lib.rs
+++ b/hercules_opt/src/lib.rs
@@ -13,7 +13,6 @@ pub mod gvn;
 pub mod inline;
 pub mod interprocedural_sroa;
 pub mod outline;
-pub mod pass;
 pub mod phi_elim;
 pub mod pred;
 pub mod schedule;
@@ -35,7 +34,6 @@ pub use crate::gvn::*;
 pub use crate::inline::*;
 pub use crate::interprocedural_sroa::*;
 pub use crate::outline::*;
-pub use crate::pass::*;
 pub use crate::phi_elim::*;
 pub use crate::pred::*;
 pub use crate::schedule::*;
diff --git a/hercules_opt/src/pass.rs b/hercules_opt/src/pass.rs
deleted file mode 100644
index 72769d72..00000000
--- a/hercules_opt/src/pass.rs
+++ /dev/null
@@ -1,1167 +0,0 @@
-use std::cell::RefCell;
-use std::collections::{HashMap, HashSet};
-use std::fs::File;
-use std::io::Write;
-use std::iter::zip;
-use std::process::{Command, Stdio};
-
-use serde::Deserialize;
-
-use tempfile::TempDir;
-
-use hercules_cg::*;
-use hercules_ir::*;
-
-use crate::*;
-
-/*
- * Passes that can be run on a module.
- */
-#[derive(Debug, Clone, Deserialize)]
-pub enum Pass {
-    DCE,
-    CCP,
-    GVN,
-    PhiElim,
-    Forkify,
-    ForkGuardElim,
-    SLF,
-    WritePredication,
-    Predication,
-    SROA,
-    Inline,
-    Outline,
-    InterproceduralSROA,
-    DeleteUncalled,
-    ForkSplit,
-    Unforkify,
-    InferSchedules,
-    GCM,
-    FloatCollections,
-    Verify,
-    // Parameterized over whether analyses that aid visualization are necessary.
-    // Useful to set to false if displaying a potentially broken module.
-    Xdot(bool),
-    // Parameterized over output directory and module name.
-    Codegen(String, String),
-    // Parameterized over where to serialize module to.
-    Serialize(String),
-}
-
-/*
- * Manages passes to be run on an IR module. Transparently handles analysis
- * requirements for optimizations.
- */
-#[derive(Debug, Clone)]
-pub struct PassManager {
-    module: Module,
-
-    // Passes to run.
-    passes: Vec<Pass>,
-
-    // Cached analysis results.
-    pub def_uses: Option<Vec<ImmutableDefUseMap>>,
-    pub reverse_postorders: Option<Vec<Vec<NodeID>>>,
-    pub typing: Option<ModuleTyping>,
-    pub control_subgraphs: Option<Vec<Subgraph>>,
-    pub doms: Option<Vec<DomTree>>,
-    pub postdoms: Option<Vec<DomTree>>,
-    pub fork_join_maps: Option<Vec<HashMap<NodeID, NodeID>>>,
-    pub fork_join_nests: Option<Vec<HashMap<NodeID, Vec<NodeID>>>>,
-    pub loops: Option<Vec<LoopTree>>,
-    pub reduce_cycles: Option<Vec<HashMap<NodeID, HashSet<NodeID>>>>,
-    pub data_nodes_in_fork_joins: Option<Vec<HashMap<NodeID, HashSet<NodeID>>>>,
-    pub bbs: Option<Vec<BasicBlocks>>,
-    pub collection_objects: Option<CollectionObjects>,
-    pub callgraph: Option<CallGraph>,
-}
-
-impl PassManager {
-    pub fn new(module: Module) -> Self {
-        PassManager {
-            module,
-            passes: vec![],
-            def_uses: None,
-            reverse_postorders: None,
-            typing: None,
-            control_subgraphs: None,
-            doms: None,
-            postdoms: None,
-            fork_join_maps: None,
-            fork_join_nests: None,
-            loops: None,
-            reduce_cycles: None,
-            data_nodes_in_fork_joins: None,
-            bbs: None,
-            collection_objects: None,
-            callgraph: None,
-        }
-    }
-
-    pub fn add_pass(&mut self, pass: Pass) {
-        self.passes.push(pass);
-    }
-
-    pub fn make_def_uses(&mut self) {
-        if self.def_uses.is_none() {
-            self.def_uses = Some(self.module.functions.iter().map(def_use).collect());
-        }
-    }
-
-    pub fn make_reverse_postorders(&mut self) {
-        if self.reverse_postorders.is_none() {
-            self.make_def_uses();
-            self.reverse_postorders = Some(
-                self.def_uses
-                    .as_ref()
-                    .unwrap()
-                    .iter()
-                    .map(reverse_postorder)
-                    .collect(),
-            );
-        }
-    }
-
-    pub fn make_typing(&mut self) {
-        if self.typing.is_none() {
-            self.make_reverse_postorders();
-            self.typing = Some(
-                typecheck(
-                    &self.module.functions,
-                    &mut self.module.types,
-                    &self.module.constants,
-                    &mut self.module.dynamic_constants,
-                    self.reverse_postorders.as_ref().unwrap(),
-                )
-                .unwrap(),
-            );
-        }
-    }
-
-    pub fn make_control_subgraphs(&mut self) {
-        if self.control_subgraphs.is_none() {
-            self.make_def_uses();
-            self.control_subgraphs = Some(
-                zip(&self.module.functions, self.def_uses.as_ref().unwrap())
-                    .map(|(function, def_use)| control_subgraph(function, def_use))
-                    .collect(),
-            );
-        }
-    }
-
-    pub fn make_doms(&mut self) {
-        if self.doms.is_none() {
-            self.make_control_subgraphs();
-            self.doms = Some(
-                self.control_subgraphs
-                    .as_ref()
-                    .unwrap()
-                    .iter()
-                    .map(|subgraph| dominator(subgraph, NodeID::new(0)))
-                    .collect(),
-            );
-        }
-    }
-
-    pub fn make_postdoms(&mut self) {
-        if self.postdoms.is_none() {
-            self.make_control_subgraphs();
-            self.postdoms = Some(
-                zip(
-                    self.control_subgraphs.as_ref().unwrap().iter(),
-                    self.module.functions.iter(),
-                )
-                .map(|(subgraph, function)| dominator(subgraph, NodeID::new(function.nodes.len())))
-                .collect(),
-            );
-        }
-    }
-
-    pub fn make_fork_join_maps(&mut self) {
-        if self.fork_join_maps.is_none() {
-            self.make_control_subgraphs();
-            self.fork_join_maps = Some(
-                zip(
-                    self.module.functions.iter(),
-                    self.control_subgraphs.as_ref().unwrap().iter(),
-                )
-                .map(|(function, subgraph)| fork_join_map(function, subgraph))
-                .collect(),
-            );
-        }
-    }
-
-    pub fn make_fork_join_nests(&mut self) {
-        if self.fork_join_nests.is_none() {
-            self.make_doms();
-            self.make_fork_join_maps();
-            self.fork_join_nests = Some(
-                zip(
-                    self.module.functions.iter(),
-                    zip(
-                        self.doms.as_ref().unwrap().iter(),
-                        self.fork_join_maps.as_ref().unwrap().iter(),
-                    ),
-                )
-                .map(|(function, (dom, fork_join_map))| {
-                    compute_fork_join_nesting(function, dom, fork_join_map)
-                })
-                .collect(),
-            );
-        }
-    }
-
-    pub fn make_loops(&mut self) {
-        if self.loops.is_none() {
-            self.make_control_subgraphs();
-            self.make_doms();
-            self.make_fork_join_maps();
-            let control_subgraphs = self.control_subgraphs.as_ref().unwrap().iter();
-            let doms = self.doms.as_ref().unwrap().iter();
-            let fork_join_maps = self.fork_join_maps.as_ref().unwrap().iter();
-            self.loops = Some(
-                zip(control_subgraphs, zip(doms, fork_join_maps))
-                    .map(|(control_subgraph, (dom, fork_join_map))| {
-                        loops(control_subgraph, NodeID::new(0), dom, fork_join_map)
-                    })
-                    .collect(),
-            );
-        }
-    }
-
-    pub fn make_reduce_cycles(&mut self) {
-        if self.reduce_cycles.is_none() {
-            self.make_def_uses();
-            let def_uses = self.def_uses.as_ref().unwrap().iter();
-            self.reduce_cycles = Some(
-                zip(self.module.functions.iter(), def_uses)
-                    .map(|(function, def_use)| reduce_cycles(function, def_use))
-                    .collect(),
-            );
-        }
-    }
-
-    pub fn make_data_nodes_in_fork_joins(&mut self) {
-        if self.data_nodes_in_fork_joins.is_none() {
-            self.make_def_uses();
-            self.make_fork_join_maps();
-            self.data_nodes_in_fork_joins = Some(
-                zip(
-                    self.module.functions.iter(),
-                    zip(
-                        self.def_uses.as_ref().unwrap().iter(),
-                        self.fork_join_maps.as_ref().unwrap().iter(),
-                    ),
-                )
-                .map(|(function, (def_use, fork_join_map))| {
-                    data_nodes_in_fork_joins(function, def_use, fork_join_map)
-                })
-                .collect(),
-            );
-        }
-    }
-
-    pub fn make_collection_objects(&mut self) {
-        if self.collection_objects.is_none() {
-            self.make_reverse_postorders();
-            self.make_typing();
-            self.make_callgraph();
-            let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
-            let typing = self.typing.as_ref().unwrap();
-            let callgraph = self.callgraph.as_ref().unwrap();
-            self.collection_objects = Some(collection_objects(
-                &self.module.functions,
-                &self.module.types,
-                reverse_postorders,
-                typing,
-                callgraph,
-            ));
-        }
-    }
-
-    pub fn make_callgraph(&mut self) {
-        if self.callgraph.is_none() {
-            self.callgraph = Some(callgraph(&self.module.functions));
-        }
-    }
-
-    pub fn run_passes(&mut self) {
-        for pass in self.passes.clone().iter() {
-            match pass {
-                Pass::DCE => {
-                    self.make_def_uses();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        dce(&mut editor);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::InterproceduralSROA => {
-                    self.make_def_uses();
-                    self.make_typing();
-
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-
-                    let def_uses = self.def_uses.as_ref().unwrap();
-
-                    let mut editors: Vec<_> = self
-                        .module
-                        .functions
-                        .iter_mut()
-                        .enumerate()
-                        .map(|(i, f)| {
-                            FunctionEditor::new(
-                                f,
-                                FunctionID::new(i),
-                                &constants_ref,
-                                &dynamic_constants_ref,
-                                &types_ref,
-                                &labels_ref,
-                                &def_uses[i],
-                            )
-                        })
-                        .collect();
-
-                    interprocedural_sroa(&mut editors);
-
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-
-                    for func in self.module.functions.iter_mut() {
-                        func.delete_gravestones();
-                    }
-
-                    self.clear_analyses();
-                }
-                Pass::CCP => {
-                    self.make_def_uses();
-                    self.make_reverse_postorders();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        ccp(&mut editor, &reverse_postorders[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::GVN => {
-                    self.make_def_uses();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        gvn(&mut editor, false);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Forkify => {
-                    self.make_def_uses();
-                    self.make_loops();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let loops = self.loops.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        forkify(
-                            &mut self.module.functions[idx],
-                            &self.module.constants,
-                            &mut self.module.dynamic_constants,
-                            &def_uses[idx],
-                            &loops[idx],
-                        );
-                        let num_nodes = self.module.functions[idx].nodes.len();
-                        self.module.functions[idx]
-                            .schedules
-                            .resize(num_nodes, vec![]);
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::PhiElim => {
-                    self.make_def_uses();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        phi_elim(&mut editor);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::ForkGuardElim => {
-                    self.make_def_uses();
-                    self.make_fork_join_maps();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        fork_guard_elim(
-                            &mut self.module.functions[idx],
-                            &self.module.constants,
-                            &fork_join_maps[idx],
-                            &def_uses[idx],
-                        );
-                        let num_nodes = self.module.functions[idx].nodes.len();
-                        self.module.functions[idx]
-                            .schedules
-                            .resize(num_nodes, vec![]);
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::SLF => {
-                    self.make_def_uses();
-                    self.make_reverse_postorders();
-                    self.make_typing();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        slf(&mut editor, &reverse_postorders[idx], &typing[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        println!("{}", self.module.functions[idx].name);
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::WritePredication => {
-                    self.make_def_uses();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        write_predication(&mut editor);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Predication => {
-                    self.make_def_uses();
-                    self.make_typing();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        predication(&mut editor, &typing[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::SROA => {
-                    self.make_def_uses();
-                    self.make_reverse_postorders();
-                    self.make_typing();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        sroa(&mut editor, &reverse_postorders[idx], &typing[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Inline => {
-                    self.make_def_uses();
-                    self.make_callgraph();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let callgraph = self.callgraph.as_ref().unwrap();
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                    let mut editors: Vec<_> = zip(
-                        self.module.functions.iter_mut().enumerate(),
-                        def_uses.iter(),
-                    )
-                    .map(|((idx, func), def_use)| {
-                        FunctionEditor::new(
-                            func,
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            def_use,
-                        )
-                    })
-                    .collect();
-                    inline(&mut editors, callgraph);
-
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-
-                    for func in self.module.functions.iter_mut() {
-                        func.delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Outline => {
-                    self.make_def_uses();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                    let old_num_funcs = self.module.functions.len();
-                    let mut editors: Vec<_> = zip(
-                        self.module.functions.iter_mut().enumerate(),
-                        def_uses.iter(),
-                    )
-                    .map(|((idx, func), def_use)| {
-                        FunctionEditor::new(
-                            func,
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            def_use,
-                        )
-                    })
-                    .collect();
-                    for editor in editors.iter_mut() {
-                        collapse_returns(editor);
-                        ensure_between_control_flow(editor);
-                    }
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-                    self.clear_analyses();
-
-                    self.make_def_uses();
-                    self.make_typing();
-                    self.make_control_subgraphs();
-                    self.make_doms();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    let control_subgraphs = self.control_subgraphs.as_ref().unwrap();
-                    let doms = self.doms.as_ref().unwrap();
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                    let mut editors: Vec<_> = zip(
-                        self.module.functions.iter_mut().enumerate(),
-                        def_uses.iter(),
-                    )
-                    .map(|((idx, func), def_use)| {
-                        FunctionEditor::new(
-                            func,
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            def_use,
-                        )
-                    })
-                    .collect();
-                    let mut new_funcs = vec![];
-                    for (idx, editor) in editors.iter_mut().enumerate() {
-                        let new_func_id = FunctionID::new(old_num_funcs + new_funcs.len());
-                        let new_func = dumb_outline(
-                            editor,
-                            &typing[idx],
-                            &control_subgraphs[idx],
-                            &doms[idx],
-                            new_func_id,
-                        );
-                        if let Some(new_func) = new_func {
-                            new_funcs.push(new_func);
-                        }
-                    }
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-
-                    for func in self.module.functions.iter_mut() {
-                        func.delete_gravestones();
-                    }
-                    self.module.functions.extend(new_funcs);
-                    self.clear_analyses();
-                }
-                Pass::DeleteUncalled => {
-                    self.make_def_uses();
-                    self.make_callgraph();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let callgraph = self.callgraph.as_ref().unwrap();
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-
-                    // By default in an editor all nodes are mutable, which is desired in this case
-                    // since we are only modifying the IDs of functions that we call.
-                    let mut editors: Vec<_> = zip(
-                        self.module.functions.iter_mut().enumerate(),
-                        def_uses.iter(),
-                    )
-                    .map(|((idx, func), def_use)| {
-                        FunctionEditor::new(
-                            func,
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            def_use,
-                        )
-                    })
-                    .collect();
-
-                    let new_idx = delete_uncalled(&mut editors, callgraph);
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-
-                    for func in self.module.functions.iter_mut() {
-                        func.delete_gravestones();
-                    }
-
-                    self.fix_deleted_functions(&new_idx);
-                    self.clear_analyses();
-
-                    assert!(self.module.functions.len() > 0, "PANIC: There are no entry functions in the Hercules module being compiled, and they all got deleted by DeleteUncalled. Please mark at least one function as an entry!");
-                }
-                Pass::ForkSplit => {
-                    self.make_def_uses();
-                    self.make_fork_join_maps();
-                    self.make_reduce_cycles();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
-                    let reduce_cycles = self.reduce_cycles.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        fork_split(&mut editor, &fork_join_maps[idx], &reduce_cycles[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Unforkify => {
-                    self.make_def_uses();
-                    self.make_fork_join_maps();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        unforkify(&mut editor, &fork_join_maps[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::GCM => loop {
-                    self.make_def_uses();
-                    self.make_reverse_postorders();
-                    self.make_typing();
-                    self.make_control_subgraphs();
-                    self.make_doms();
-                    self.make_fork_join_maps();
-                    self.make_loops();
-                    self.make_collection_objects();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let reverse_postorders = self.reverse_postorders.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    let doms = self.doms.as_ref().unwrap();
-                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
-                    let loops = self.loops.as_ref().unwrap();
-                    let control_subgraphs = self.control_subgraphs.as_ref().unwrap();
-                    let collection_objects = self.collection_objects.as_ref().unwrap();
-                    let mut bbs = vec![];
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        if let Some(bb) = gcm(
-                            &mut editor,
-                            &def_uses[idx],
-                            &reverse_postorders[idx],
-                            &typing[idx],
-                            &control_subgraphs[idx],
-                            &doms[idx],
-                            &fork_join_maps[idx],
-                            &loops[idx],
-                            collection_objects,
-                        ) {
-                            bbs.push(bb);
-                        }
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                    if bbs.len() == self.module.functions.len() {
-                        self.bbs = Some(bbs);
-                        break;
-                    }
-                },
-                Pass::FloatCollections => {
-                    self.make_def_uses();
-                    self.make_typing();
-                    self.make_callgraph();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let typing = self.typing.as_ref().unwrap();
-                    let callgraph = self.callgraph.as_ref().unwrap();
-                    let devices = device_placement(&self.module.functions, &callgraph);
-                    let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
-                    let dynamic_constants_ref =
-                        RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                    let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                    let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                    let mut editors: Vec<_> = zip(
-                        self.module.functions.iter_mut().enumerate(),
-                        def_uses.iter(),
-                    )
-                    .map(|((idx, func), def_use)| {
-                        FunctionEditor::new(
-                            func,
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            def_use,
-                        )
-                    })
-                    .collect();
-                    float_collections(&mut editors, typing, callgraph, &devices);
-
-                    self.module.constants = constants_ref.take();
-                    self.module.dynamic_constants = dynamic_constants_ref.take();
-                    self.module.types = types_ref.take();
-                    self.module.labels = labels_ref.take();
-
-                    for func in self.module.functions.iter_mut() {
-                        func.delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::InferSchedules => {
-                    self.make_def_uses();
-                    self.make_fork_join_maps();
-                    self.make_reduce_cycles();
-                    let def_uses = self.def_uses.as_ref().unwrap();
-                    let fork_join_maps = self.fork_join_maps.as_ref().unwrap();
-                    let reduce_cycles = self.reduce_cycles.as_ref().unwrap();
-                    for idx in 0..self.module.functions.len() {
-                        let constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.constants));
-                        let dynamic_constants_ref =
-                            RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
-                        let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
-                        let labels_ref = RefCell::new(std::mem::take(&mut self.module.labels));
-                        let mut editor = FunctionEditor::new(
-                            &mut self.module.functions[idx],
-                            FunctionID::new(idx),
-                            &constants_ref,
-                            &dynamic_constants_ref,
-                            &types_ref,
-                            &labels_ref,
-                            &def_uses[idx],
-                        );
-                        infer_parallel_reduce(
-                            &mut editor,
-                            &fork_join_maps[idx],
-                            &reduce_cycles[idx],
-                        );
-                        infer_parallel_fork(&mut editor, &fork_join_maps[idx]);
-                        infer_vectorizable(&mut editor, &fork_join_maps[idx]);
-                        infer_tight_associative(&mut editor, &reduce_cycles[idx]);
-
-                        self.module.constants = constants_ref.take();
-                        self.module.dynamic_constants = dynamic_constants_ref.take();
-                        self.module.types = types_ref.take();
-                        self.module.labels = labels_ref.take();
-
-                        self.module.functions[idx].delete_gravestones();
-                    }
-                    self.clear_analyses();
-                }
-                Pass::Verify => {
-                    let (
-                        def_uses,
-                        reverse_postorders,
-                        typing,
-                        subgraphs,
-                        doms,
-                        postdoms,
-                        fork_join_maps,
-                    ) = verify(&mut self.module)
-                        .expect("PANIC: Failed to verify Hercules IR module.");
-
-                    // Verification produces a bunch of analysis results that
-                    // may be useful for later passes.
-                    self.def_uses = Some(def_uses);
-                    self.reverse_postorders = Some(reverse_postorders);
-                    self.typing = Some(typing);
-                    self.control_subgraphs = Some(subgraphs);
-                    self.doms = Some(doms);
-                    self.postdoms = Some(postdoms);
-                    self.fork_join_maps = Some(fork_join_maps);
-                }
-                Pass::Xdot(force_analyses) => {
-                    self.make_reverse_postorders();
-                    if *force_analyses {
-                        self.make_doms();
-                        self.make_fork_join_maps();
-                    }
-                    xdot_module(
-                        &self.module,
-                        self.reverse_postorders.as_ref().unwrap(),
-                        self.doms.as_ref(),
-                        self.fork_join_maps.as_ref(),
-                    );
-                }
-                Pass::Codegen(output_dir, module_name) => {
-                    self.make_typing();
-                    self.make_control_subgraphs();
-                    self.make_collection_objects();
-                    self.make_callgraph();
-                    let typing = self.typing.as_ref().unwrap();
-                    let control_subgraphs = self.control_subgraphs.as_ref().unwrap();
-                    let bbs = self.bbs.as_ref().unwrap();
-                    let collection_objects = self.collection_objects.as_ref().unwrap();
-                    let callgraph = self.callgraph.as_ref().unwrap();
-
-                    let devices = device_placement(&self.module.functions, &callgraph);
-
-                    let mut rust_rt = String::new();
-                    let mut llvm_ir = String::new();
-                    for idx in 0..self.module.functions.len() {
-                        match devices[idx] {
-                            Device::LLVM => cpu_codegen(
-                                &self.module.functions[idx],
-                                &self.module.types,
-                                &self.module.constants,
-                                &self.module.dynamic_constants,
-                                &typing[idx],
-                                &control_subgraphs[idx],
-                                &bbs[idx],
-                                &mut llvm_ir,
-                            )
-                            .unwrap(),
-                            Device::AsyncRust => rt_codegen(
-                                FunctionID::new(idx),
-                                &self.module,
-                                &typing[idx],
-                                &control_subgraphs[idx],
-                                &bbs[idx],
-                                &collection_objects,
-                                &callgraph,
-                                &devices,
-                                &mut rust_rt,
-                            )
-                            .unwrap(),
-                            _ => todo!(),
-                        }
-                    }
-                    println!("{}", llvm_ir);
-                    println!("{}", rust_rt);
-
-                    // Write the LLVM IR into a temporary file.
-                    let tmp_dir = TempDir::new().unwrap();
-                    let mut tmp_path = tmp_dir.path().to_path_buf();
-                    tmp_path.push(format!("{}.ll", module_name));
-                    println!("{}", tmp_path.display());
-                    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.");
-
-                    // Compile LLVM IR into an ELF object file.
-                    let output_archive = format!("{}/lib{}.a", output_dir, module_name);
-                    println!("{}", output_archive);
-                    let mut clang_process = Command::new("clang")
-                        .arg(&tmp_path)
-                        .arg("--emit-static-lib")
-                        .arg("-O3")
-                        .arg("-march=native")
-                        .arg("-o")
-                        .arg(&output_archive)
-                        .stdin(Stdio::piped())
-                        .stdout(Stdio::piped())
-                        .spawn()
-                        .expect("Error running clang. Is it installed?");
-                    assert!(clang_process.wait().unwrap().success());
-
-                    // Write the Rust runtime into a file.
-                    let output_rt = format!("{}/rt_{}.hrt", output_dir, module_name);
-                    println!("{}", output_rt);
-                    let mut file = File::create(&output_rt)
-                        .expect("PANIC: Unable to open output Rust runtime file.");
-                    file.write_all(rust_rt.as_bytes())
-                        .expect("PANIC: Unable to write output Rust runtime file contents.");
-                }
-                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.");
-                }
-            }
-            eprintln!("Ran pass: {:?}", pass);
-        }
-    }
-
-    fn clear_analyses(&mut self) {
-        self.def_uses = None;
-        self.reverse_postorders = None;
-        self.typing = None;
-        self.control_subgraphs = None;
-        self.doms = None;
-        self.postdoms = None;
-        self.fork_join_maps = None;
-        self.fork_join_nests = None;
-        self.loops = None;
-        self.reduce_cycles = None;
-        self.data_nodes_in_fork_joins = None;
-        self.bbs = None;
-        self.collection_objects = None;
-        self.callgraph = None;
-    }
-
-    pub fn get_module(self) -> Module {
-        self.module
-    }
-
-    fn fix_deleted_functions(&mut self, id_mapping: &[Option<usize>]) {
-        let mut idx = 0;
-
-        // Rust does not like enumerate here, so use
-        // idx outside as a hack to make it happy.
-        self.module.functions.retain(|_| {
-            idx += 1;
-            id_mapping[idx - 1].is_some()
-        });
-    }
-}
diff --git a/hercules_tools/hercules_driver/Cargo.toml b/hercules_tools/hercules_driver/Cargo.toml
deleted file mode 100644
index ad9397b1..00000000
--- a/hercules_tools/hercules_driver/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "hercules_driver"
-version = "0.1.0"
-authors = ["Russel Arbore <rarbore2@illinois.edu>"]
-edition = "2021"
-
-[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
deleted file mode 100644
index a2550022..00000000
--- a/hercules_tools/hercules_driver/src/main.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::Path;
-
-use clap::Parser;
-
-#[derive(Parser, Debug)]
-#[command(author, version, about, long_about = None)]
-struct Args {
-    file: String,
-    passes: String,
-}
-
-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.");
-    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
-        .passes
-        .split(char::is_whitespace)
-        .map(|pass_str| {
-            assert_ne!(
-                pass_str, "",
-                "PANIC: Can't interpret empty pass name. Try giving a list of pass names."
-            );
-            ron::from_str(pass_str).expect("PANIC: Couldn't parse list of passes.")
-        })
-        .collect();
-    for pass in passes {
-        pm.add_pass(pass);
-    }
-    pm.run_passes();
-}
-- 
GitLab


From f72920569c41960b01728b88be3073309a8ebeaa Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 22 Jan 2025 14:06:42 -0600
Subject: [PATCH 41/46] Formatting

---
 juno_scheduler/src/pm.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 15cb38b2..7cd68647 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -6,8 +6,8 @@ use hercules_opt::FunctionEditor;
 use hercules_opt::{
     ccp, collapse_returns, dce, dumb_outline, ensure_between_control_flow, float_collections,
     fork_split, gcm, gvn, infer_parallel_fork, infer_parallel_reduce, infer_tight_associative,
-    infer_vectorizable, inline, interprocedural_sroa, outline, phi_elim, predication, sroa,
-    unforkify, write_predication, slf,
+    infer_vectorizable, inline, interprocedural_sroa, outline, phi_elim, predication, slf, sroa,
+    unforkify, write_predication,
 };
 
 use tempfile::TempDir;
-- 
GitLab


From 52e9ee6f040b3784a9fbabf56df439e4b2c459fd Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 22 Jan 2025 14:18:24 -0600
Subject: [PATCH 42/46] Expand example schedule

---
 juno_samples/schedule_test/src/sched.sch | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index ad2cc443..23041fd5 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -15,13 +15,17 @@ macro codegen-prep!(X) {
 
 
 juno-setup!(*);
-outline(test@outer);
-outline(test@row);
 
-gvn(*);
+let first = outline(test@outer);
+let second = outline(test@row);
+
+// We can use the functions produced by outlining in our schedules
+gvn(first, second, test);
+
 ip-sroa(*);
 sroa(*);
 phi-elim(*);
 
+
 codegen-prep!(*);
 //xdot[true](*);
-- 
GitLab


From 382d4a65fabeb846d9c64c084990136558c6f1c0 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 22 Jan 2025 14:23:57 -0600
Subject: [PATCH 43/46] Expand example schedule

---
 juno_samples/schedule_test/src/sched.sch | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/juno_samples/schedule_test/src/sched.sch b/juno_samples/schedule_test/src/sched.sch
index 23041fd5..f73e3e70 100644
--- a/juno_samples/schedule_test/src/sched.sch
+++ b/juno_samples/schedule_test/src/sched.sch
@@ -24,8 +24,20 @@ gvn(first, second, test);
 
 ip-sroa(*);
 sroa(*);
-phi-elim(*);
 
+// We can evaluate expressions using labels and save them for later use
+let inner = first@inner;
+
+// A fixpoint can run a (series) of passes until no more changes are made
+// (though some passes seem to make edits even if there are no real changes,
+// so this is fragile).
+// We could just let it run until it converges but can also tell it to panic
+// if it hasn't converged after a number of iterations (like here) tell it to
+// just stop after a certain number of iterations (stop after #) or to print
+// the iteration number (print iter)
+fixpoint panic after 2 {
+  phi-elim(*);
+}
 
 codegen-prep!(*);
 //xdot[true](*);
-- 
GitLab


From 3c7a6fdfb5546bf0d920fc4dd26f670061033076 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Wed, 22 Jan 2025 17:12:07 -0600
Subject: [PATCH 44/46] Some cleanup

---
 hercules_ir/src/build.rs             | 11 +++++++----
 hercules_ir/src/ir.rs                | 10 +++++-----
 hercules_opt/src/editor.rs           | 20 ++++++++++++--------
 juno_frontend/src/labeled_builder.rs |  2 +-
 juno_scheduler/src/pm.rs             |  8 ++++----
 5 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index 335f3a75..1dd326c3 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -38,7 +38,7 @@ pub struct NodeBuilder {
     function_id: FunctionID,
     node: Node,
     schedules: Vec<Schedule>,
-    labels: Vec<usize>,
+    labels: Vec<LabelID>,
 }
 
 /*
@@ -85,7 +85,7 @@ impl<'a> Builder<'a> {
         if let Some(id) = self.interned_labels.get(label) {
             *id
         } else {
-            let id = self.interned_labels.len();
+            let id = LabelID::new(self.interned_labels.len());
             self.interned_labels.insert(label.clone(), id);
             self.module.labels.push(label.clone());
             id
@@ -646,7 +646,10 @@ impl NodeBuilder {
         self.labels.push(label);
     }
 
-    pub fn add_labels(&mut self, label: Vec<LabelID>) {
-        self.labels.extend(label);
+    pub fn add_labels<I>(&mut self, labels: I)
+    where
+        I: Iterator<Item = LabelID>,
+    {
+        self.labels.extend(labels);
     }
 }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index fb2b2494..d1fdd225 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -45,8 +45,8 @@ pub struct Function {
 
     pub nodes: Vec<Node>,
 
-    pub schedules: FunctionSchedule,
-    pub labels: FunctionLabel,
+    pub schedules: FunctionSchedules,
+    pub labels: FunctionLabels,
     pub device: Option<Device>,
 }
 
@@ -344,13 +344,12 @@ pub enum Device {
 /*
  * A single node may have multiple schedules.
  */
-pub type FunctionSchedule = Vec<Vec<Schedule>>;
+pub type FunctionSchedules = Vec<Vec<Schedule>>;
 
 /*
  * A single node may have multiple labels.
  */
-pub type LabelID = usize;
-pub type FunctionLabel = Vec<HashSet<LabelID>>;
+pub type FunctionLabels = Vec<HashSet<LabelID>>;
 
 impl Module {
     /*
@@ -1777,3 +1776,4 @@ define_id_type!(NodeID);
 define_id_type!(TypeID);
 define_id_type!(ConstantID);
 define_id_type!(DynamicConstantID);
+define_id_type!(LabelID);
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index a9ee8c8d..0ff42c7d 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -58,7 +58,7 @@ pub struct FunctionEdit<'a: 'b, 'b> {
     // Keep track of added and updated schedules.
     added_and_updated_schedules: BTreeMap<NodeID, Vec<Schedule>>,
     // Keep track of added and updated labels.
-    added_and_updated_labels: BTreeMap<NodeID, HashSet<usize>>,
+    added_and_updated_labels: BTreeMap<NodeID, HashSet<LabelID>>,
     // Keep track of added (dynamic) constants, types, and labels
     added_constants: Vec<Constant>,
     added_dynamic_constants: Vec<DynamicConstant>,
@@ -197,7 +197,11 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
 
             // Step 0: determine whether the edit changed the IR by checking if
             // any nodes were deleted, added, or updated in any way
-            editor.modified |= !deleted_nodeids.is_empty() || !added_nodeids.is_empty() || !added_and_updated_nodes.is_empty() || !added_and_updated_schedules.is_empty() || !added_and_updated_labels.is_empty();
+            editor.modified |= !deleted_nodeids.is_empty()
+                || !added_nodeids.is_empty()
+                || !added_and_updated_nodes.is_empty()
+                || !added_and_updated_schedules.is_empty()
+                || !added_and_updated_labels.is_empty();
 
             // Step 1: update the mutable def use map.
             for (u, new_users) in updated_def_use {
@@ -569,19 +573,19 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
             .chain(self.added_labels.iter())
             .position(|l| *l == name);
         if let Some(idx) = pos {
-            idx
+            LabelID::new(idx)
         } else {
-            let id = self.editor.labels.borrow().len() + self.added_labels.len();
+            let idx = self.editor.labels.borrow().len() + self.added_labels.len();
             self.added_labels.push(name);
-            id
+            LabelID::new(idx)
         }
     }
 
     // Creates an entirely fresh label and returns its LabelID
     pub fn fresh_label(&mut self) -> LabelID {
-        let id = self.editor.labels.borrow().len() + self.added_labels.len();
-        self.added_labels.push(format!("#fresh_{}", id));
-        id
+        let idx = self.editor.labels.borrow().len() + self.added_labels.len();
+        self.added_labels.push(format!("#fresh_{}", idx));
+        LabelID::new(idx)
     }
 
     pub fn get_users(&self, id: NodeID) -> impl Iterator<Item = NodeID> + '_ {
diff --git a/juno_frontend/src/labeled_builder.rs b/juno_frontend/src/labeled_builder.rs
index 8ae408ed..15bed6c2 100644
--- a/juno_frontend/src/labeled_builder.rs
+++ b/juno_frontend/src/labeled_builder.rs
@@ -73,7 +73,7 @@ impl<'a> LabeledBuilder<'a> {
         };
 
         let mut builder = self.builder.allocate_node(func);
-        builder.add_labels(label);
+        builder.add_labels(label.into_iter());
         builder
     }
 
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 7cd68647..0c59ac29 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -712,14 +712,14 @@ fn interp_expr(
                 Value::JunoFunction { func } => {
                     match pm.labels.borrow().iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
-                        Some(label_id) => Ok((
+                        Some(label_idx) => Ok((
                             Value::Label {
                                 labels: functions
                                     .get_function(func)
                                     .iter()
                                     .map(|f| LabelInfo {
                                         func: *f,
-                                        label: label_id,
+                                        label: LabelID::new(label_idx),
                                     })
                                     .collect(),
                             },
@@ -730,11 +730,11 @@ fn interp_expr(
                 Value::HerculesFunction { func } => {
                     match pm.labels.borrow().iter().position(|s| s == field) {
                         None => Err(SchedulerError::UndefinedLabel(field.clone())),
-                        Some(label_id) => Ok((
+                        Some(label_idx) => Ok((
                             Value::Label {
                                 labels: vec![LabelInfo {
                                     func: func,
-                                    label: label_id,
+                                    label: LabelID::new(label_idx),
                                 }],
                             },
                             changed,
-- 
GitLab


From 71f3a8e8400135911b07d0d0b40d3b7c8991788f Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Wed, 22 Jan 2025 17:29:15 -0600
Subject: [PATCH 45/46] Copy labels in inlining

---
 hercules_opt/src/inline.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/hercules_opt/src/inline.rs b/hercules_opt/src/inline.rs
index 54af8582..064e3d73 100644
--- a/hercules_opt/src/inline.rs
+++ b/hercules_opt/src/inline.rs
@@ -209,6 +209,11 @@ fn inline_func(
                 for schedule in callee_schedule {
                     edit = edit.add_schedule(add_id, schedule.clone())?;
                 }
+                // Copy the labels from the callee.
+                let callee_labels = &called_func.labels[idx];
+                for label in callee_labels {
+                    edit = edit.add_label(add_id, *label)?;
+                }
             }
 
             // Stitch the control use of the inlined start node with the
-- 
GitLab


From 310ce0deca305715c0d704088b385c27793a0446 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 23 Jan 2025 10:02:03 -0600
Subject: [PATCH 46/46] Optimistically propagate labels

---
 hercules_opt/src/editor.rs | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 0ff42c7d..6271a958 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -273,11 +273,27 @@ impl<'a: 'b, 'b> FunctionEditor<'a> {
             // Step 5.1: update and propagate labels
             editor.labels.borrow_mut().extend(added_labels);
 
-            // TODO: Is this enough to propagate labels
+            // We propagate labels in two steps, first along sub-edits and then
+            // all the labels on any deleted node not used in any sub-edit to all
+            // added nodes not in any sub-edit
+            let mut sources = deleted_nodeids.clone();
+            let mut dests = added_nodeids.clone();
+
             for (src, dst) in sub_edits {
                 let mut dst_labels = take(&mut editor.function.labels[dst.idx()]);
                 dst_labels.extend(editor.function.labels[src.idx()].iter());
                 editor.function.labels[dst.idx()] = dst_labels;
+
+                sources.remove(&src);
+                dests.remove(&dst);
+            }
+
+            let mut src_labels = HashSet::new();
+            for src in sources {
+                src_labels.extend(editor.function.labels[src.idx()].clone());
+            }
+            for dst in dests {
+                editor.function.labels[dst.idx()].extend(src_labels.clone());
             }
 
             // Step 6: update the length of mutable_nodes. All added nodes are
-- 
GitLab