From 9b8738de63959f6daa20f8d54e7eefdf121102e4 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 09:35:04 -0600
Subject: [PATCH 01/16] Expand concat example, breaks type-checking

---
 juno_samples/concat/src/concat.jn | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/juno_samples/concat/src/concat.jn b/juno_samples/concat/src/concat.jn
index 2471671e..70c741b6 100644
--- a/juno_samples/concat/src/concat.jn
+++ b/juno_samples/concat/src/concat.jn
@@ -30,3 +30,22 @@ fn concat_entry(a : i32) -> i32 {
   let arr3 = concat::<i32, 3, 6>(arr1, arr2);
   return sum::<i32, 9>(arr3);
 }
+
+#[entry]
+fn concat_switch<n: usize>(b: i32, m: i32[n]) -> i32[n + 2] {
+  let ex : i32[2];
+  ex[0] = 0;
+  ex[1] = 1;
+
+  let x = concat::<_, 2, n>(ex, m);
+  let y = concat::<_, n, 2>(m, ex);
+
+  let s = 0;
+
+  s += sum::<i32, n + 2>(x);
+  s += sum::<i32, 2 + n>(x);
+  s += sum::<i32, n + 2>(y);
+  s += sum::<i32, 2 + n>(y);
+
+  return if s < b then x else y;
+}
-- 
GitLab


From 2a18b1cb0c0b427363d1fed9e17d1ef0ff88cca8 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 15:28:35 -0600
Subject: [PATCH 02/16] Define normalization process

---
 hercules_ir/src/dc_normalization.rs | 198 ++++++++++++++++++++++++++++
 hercules_ir/src/ir.rs               | 105 ++++++++-------
 hercules_ir/src/lib.rs              |   2 +
 3 files changed, 255 insertions(+), 50 deletions(-)
 create mode 100644 hercules_ir/src/dc_normalization.rs

diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
new file mode 100644
index 00000000..cfeaf744
--- /dev/null
+++ b/hercules_ir/src/dc_normalization.rs
@@ -0,0 +1,198 @@
+use crate::*;
+
+use std::collections::HashMap;
+use std::cmp::{min, max};
+
+fn insert_dc(
+    dc: DynamicConstant,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    if let Some(id) = reverse_map.get(&dc) {
+        *id
+    } else {
+        let id = DynamicConstantID::new(dynamic_constants.len());
+        dynamic_constants.push(dc.clone());
+        reverse_map.insert(dc, id);
+        id
+    }
+}
+
+pub fn dc_add(
+    dcs: Vec<DynamicConstantID>,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    let mut constant_val = 0;
+    let mut fields = vec![];
+
+    for dc in dcs {
+        match &dynamic_constants[dc.idx()] {
+            DynamicConstant::Constant(x) => constant_val += x,
+            DynamicConstant::Add(xs) => fields.extend_from_slice(xs),
+            _ => fields.push(dc),
+        }
+    }
+
+    let new_dc =
+        if fields.len() == 0 {
+            // If there are no non-constants, our result is a constant
+            DynamicConstant::Constant(constant_val)
+        } else if constant_val == 0 {
+            // If there are non-constants and our constant value is zero, omit it
+            fields.sort();
+            DynamicConstant::Add(fields)
+        } else {
+            // Both non-constant and constant pieces
+            fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+            fields.sort();
+            DynamicConstant::Add(fields)
+        };
+
+    insert_dc(new_dc, dynamic_constants, reverse_map)
+}
+
+pub fn dc_mul(
+    dcs: Vec<DynamicConstantID>,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    let mut constant_val = 1;
+    let mut fields = vec![];
+
+    for dc in dcs {
+        match &dynamic_constants[dc.idx()] {
+            DynamicConstant::Constant(x) => constant_val *= x,
+            DynamicConstant::Mul(xs) => fields.extend_from_slice(xs),
+            _ => fields.push(dc),
+        }
+    }
+
+    let new_dc =
+        if fields.len() == 0 {
+            // If there are no non-constants, our result is a constant
+            DynamicConstant::Constant(constant_val)
+        } else if constant_val == 1 {
+            // If there are non-constants and our constant value is one, omit it
+            fields.sort();
+            DynamicConstant::Mul(fields)
+        } else {
+            // Both non-constant and constant pieces
+            fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+            fields.sort();
+            DynamicConstant::Mul(fields)
+        };
+
+    insert_dc(new_dc, dynamic_constants, reverse_map)
+}
+
+pub fn dc_min(
+    dcs: Vec<DynamicConstantID>,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    let mut constant_val : Option<usize> = None;
+    let mut fields = vec![];
+
+    for dc in dcs {
+        match &dynamic_constants[dc.idx()] {
+            DynamicConstant::Constant(x) => {
+                if let Some(cur_min) = constant_val {
+                    constant_val = Some(min(cur_min, *x));
+                } else {
+                    constant_val = Some(*x);
+                }
+            }
+            DynamicConstant::Min(xs) => fields.extend_from_slice(xs),
+            _ => fields.push(dc),
+        }
+    }
+
+    let new_dc =
+        if let Some(const_val) = constant_val {
+            fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+            fields.sort();
+            DynamicConstant::Min(fields)
+        } else {
+            fields.sort();
+            DynamicConstant::Min(fields)
+        };
+
+    insert_dc(new_dc, dynamic_constants, reverse_map)
+}
+
+pub fn dc_max(
+    dcs: Vec<DynamicConstantID>,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    let mut constant_val : Option<usize> = None;
+    let mut fields = vec![];
+
+    for dc in dcs {
+        match &dynamic_constants[dc.idx()] {
+            DynamicConstant::Constant(x) => {
+                if let Some(cur_max) = constant_val {
+                    constant_val = Some(max(cur_max, *x));
+                } else {
+                    constant_val = Some(*x);
+                }
+            }
+            DynamicConstant::Max(xs) => fields.extend_from_slice(xs),
+            _ => fields.push(dc),
+        }
+    }
+
+    let new_dc =
+        if let Some(const_val) = constant_val {
+            fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+            fields.sort();
+            DynamicConstant::Max(fields)
+        } else {
+            fields.sort();
+            DynamicConstant::Max(fields)
+        };
+
+    insert_dc(new_dc, dynamic_constants, reverse_map)
+}
+
+pub fn dc_sub(
+    x: DynamicConstantID,
+    y: DynamicConstantID,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+            insert_dc(DynamicConstant::Constant(x - y), dynamic_constants, reverse_map),
+        (_, DynamicConstant::Constant(0)) => x,
+        _ => insert_dc(DynamicConstant::Sub(x, y), dynamic_constants, reverse_map),
+    }
+}
+
+pub fn dc_div(
+    x: DynamicConstantID,
+    y: DynamicConstantID,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+            insert_dc(DynamicConstant::Constant(x / y), dynamic_constants, reverse_map),
+        (_, DynamicConstant::Constant(1)) => x,
+        _ => insert_dc(DynamicConstant::Div(x, y), dynamic_constants, reverse_map),
+    }
+}
+
+pub fn dc_rem(
+    x: DynamicConstantID,
+    y: DynamicConstantID,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+            insert_dc(DynamicConstant::Constant(x % y), dynamic_constants, reverse_map),
+        _ => insert_dc(DynamicConstant::Rem(x, y), dynamic_constants, reverse_map),
+    }
+}
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index d8a124e2..9dcd768c 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -1,4 +1,3 @@
-use std::cmp::{max, min};
 use std::collections::HashSet;
 use std::fmt::Write;
 use std::ops::Coroutine;
@@ -120,13 +119,14 @@ pub enum DynamicConstant {
     // function is this).
     Parameter(usize),
     // Supported integer operations on dynamic constants.
-    Add(DynamicConstantID, DynamicConstantID),
+    Add(Vec<DynamicConstantID>),
+    Mul(Vec<DynamicConstantID>),
+    Min(Vec<DynamicConstantID>),
+    Max(Vec<DynamicConstantID>),
+
     Sub(DynamicConstantID, DynamicConstantID),
-    Mul(DynamicConstantID, DynamicConstantID),
     Div(DynamicConstantID, DynamicConstantID),
     Rem(DynamicConstantID, DynamicConstantID),
-    Min(DynamicConstantID, DynamicConstantID),
-    Max(DynamicConstantID, DynamicConstantID),
 }
 
 /*
@@ -464,21 +464,31 @@ impl Module {
         match &self.dynamic_constants[dc_id.idx()] {
             DynamicConstant::Constant(cons) => write!(w, "{}", cons),
             DynamicConstant::Parameter(param) => write!(w, "#{}", param),
-            DynamicConstant::Add(x, y)
-            | DynamicConstant::Sub(x, y)
-            | DynamicConstant::Mul(x, y)
+            DynamicConstant::Add(xs)
+            | DynamicConstant::Mul(xs)
+            | DynamicConstant::Min(xs)
+            | DynamicConstant::Max(xs) => {
+                match &self.dynamic_constants[dc_id.idx()] {
+                    DynamicConstant::Add(_) => write!(w, "+")?,
+                    DynamicConstant::Mul(_) => write!(w, "*")?,
+                    DynamicConstant::Min(_) => write!(w, "min")?,
+                    DynamicConstant::Max(_) => write!(w, "max")?,
+                    _ => (),
+                }
+                write!(w, "(")?;
+                for arg in xs {
+                    self.write_dynamic_constant(*arg, w)?;
+                    write!(w, ",")?;
+                }
+                write!(w, ")")
+            }
+            DynamicConstant::Sub(x, y)
             | DynamicConstant::Div(x, y)
-            | DynamicConstant::Rem(x, y)
-            | DynamicConstant::Min(x, y)
-            | DynamicConstant::Max(x, y) => {
+            | DynamicConstant::Rem(x, y) => {
                 match &self.dynamic_constants[dc_id.idx()] {
-                    DynamicConstant::Add(_, _) => write!(w, "+")?,
                     DynamicConstant::Sub(_, _) => write!(w, "-")?,
-                    DynamicConstant::Mul(_, _) => write!(w, "*")?,
                     DynamicConstant::Div(_, _) => write!(w, "/")?,
                     DynamicConstant::Rem(_, _) => write!(w, "%")?,
-                    DynamicConstant::Min(_, _) => write!(w, "min")?,
-                    DynamicConstant::Max(_, _) => write!(w, "max")?,
                     _ => (),
                 }
                 write!(w, "(")?;
@@ -638,15 +648,33 @@ pub fn dynamic_constants_bottom_up(
             if visited[id.idx()] {
                 continue;
             }
-            match dynamic_constants[id.idx()] {
-                DynamicConstant::Add(left, right)
-                | DynamicConstant::Sub(left, right)
-                | DynamicConstant::Mul(left, right)
-                | DynamicConstant::Div(left, right)
-                | DynamicConstant::Rem(left, right) => {
+            match &dynamic_constants[id.idx()] {
+                DynamicConstant::Add(args)
+                | DynamicConstant::Mul(args)
+                | DynamicConstant::Min(args)
+                | DynamicConstant::Max(args) => {
                     // We have to yield the children of this node before
                     // this node itself. We keep track of which nodes have
                     // yielded using visited.
+                    if args.iter().any(|i| i.idx() >= visited.len()) {
+                        // Some argument is an invalid dynamic constant and should be skipped
+                        continue;
+                    }
+
+                    if args.iter().all(|i| visited[i.idx()]) {
+                        // Since all children have been yielded, we yield ourself
+                        visited.set(id.idx(), true);
+                        yield id;
+                    } else {
+                        // Otherwise push self onto stack so that the children will get popped
+                        // first
+                        stack.push(id);
+                        stack.extend(args.clone());
+                    }
+                }
+                DynamicConstant::Sub(left, right)
+                | DynamicConstant::Div(left, right)
+                | DynamicConstant::Rem(left, right) => {
                     if left.idx() >= visited.len() || right.idx() >= visited.len() {
                         // This is an invalid dynamic constant and should be
                         // skipped.
@@ -660,8 +688,8 @@ pub fn dynamic_constants_bottom_up(
                         // Push ourselves, then children, so that children
                         // get popped first.
                         stack.push(id);
-                        stack.push(left);
-                        stack.push(right);
+                        stack.push(*left);
+                        stack.push(*right);
                     }
                 }
                 _ => {
@@ -1024,33 +1052,10 @@ pub fn evaluate_dynamic_constant(
     cons: DynamicConstantID,
     dcs: &Vec<DynamicConstant>,
 ) -> Option<usize> {
-    match dcs[cons.idx()] {
-        DynamicConstant::Constant(cons) => Some(cons),
-        DynamicConstant::Parameter(_) => None,
-        DynamicConstant::Add(left, right) => {
-            Some(evaluate_dynamic_constant(left, dcs)? + evaluate_dynamic_constant(right, dcs)?)
-        }
-        DynamicConstant::Sub(left, right) => {
-            Some(evaluate_dynamic_constant(left, dcs)? - evaluate_dynamic_constant(right, dcs)?)
-        }
-        DynamicConstant::Mul(left, right) => {
-            Some(evaluate_dynamic_constant(left, dcs)? * evaluate_dynamic_constant(right, dcs)?)
-        }
-        DynamicConstant::Div(left, right) => {
-            Some(evaluate_dynamic_constant(left, dcs)? / evaluate_dynamic_constant(right, dcs)?)
-        }
-        DynamicConstant::Rem(left, right) => {
-            Some(evaluate_dynamic_constant(left, dcs)? % evaluate_dynamic_constant(right, dcs)?)
-        }
-        DynamicConstant::Min(left, right) => Some(min(
-            evaluate_dynamic_constant(left, dcs)?,
-            evaluate_dynamic_constant(right, dcs)?,
-        )),
-        DynamicConstant::Max(left, right) => Some(max(
-            evaluate_dynamic_constant(left, dcs)?,
-            evaluate_dynamic_constant(right, dcs)?,
-        )),
-    }
+    // Because of normalization, if a dynamic constant can be expressed as a constant it must be a
+    // constant
+    let DynamicConstant::Constant(cons) = dcs[cons.idx()] else { return None; };
+    Some(cons)
 }
 
 /*
diff --git a/hercules_ir/src/lib.rs b/hercules_ir/src/lib.rs
index 85dc277f..bc1fbb9f 100644
--- a/hercules_ir/src/lib.rs
+++ b/hercules_ir/src/lib.rs
@@ -10,6 +10,7 @@ pub mod build;
 pub mod callgraph;
 pub mod collections;
 pub mod dataflow;
+pub mod dc_normalization;
 pub mod def_use;
 pub mod device;
 pub mod dom;
@@ -26,6 +27,7 @@ pub use crate::build::*;
 pub use crate::callgraph::*;
 pub use crate::collections::*;
 pub use crate::dataflow::*;
+pub use crate::dc_normalization::*;
 pub use crate::def_use::*;
 pub use crate::device::*;
 pub use crate::dom::*;
-- 
GitLab


From 6beb6f4389d7f311ffaa41438269ee3279e162ac Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 15:59:20 -0600
Subject: [PATCH 03/16] Normalize DCs if there's only one term

---
 hercules_ir/src/dc_normalization.rs | 116 +++++++++++++++-------------
 1 file changed, 64 insertions(+), 52 deletions(-)

diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index cfeaf744..55effa3b 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -18,6 +18,22 @@ fn insert_dc(
     }
 }
 
+pub fn dc_const(
+    val: usize,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    insert_dc(DynamicConstant::Constant(val), dynamic_constants, reverse_map)
+}
+
+pub fn dc_param(
+    index: usize,
+    dynamic_constants: &mut Vec<DynamicConstant>,
+    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+) -> DynamicConstantID {
+    insert_dc(DynamicConstant::Parameter(index), dynamic_constants, reverse_map)
+}
+
 pub fn dc_add(
     dcs: Vec<DynamicConstantID>,
     dynamic_constants: &mut Vec<DynamicConstant>,
@@ -34,22 +50,18 @@ pub fn dc_add(
         }
     }
 
-    let new_dc =
-        if fields.len() == 0 {
-            // If there are no non-constants, our result is a constant
-            DynamicConstant::Constant(constant_val)
-        } else if constant_val == 0 {
-            // If there are non-constants and our constant value is zero, omit it
-            fields.sort();
-            DynamicConstant::Add(fields)
-        } else {
-            // Both non-constant and constant pieces
-            fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
-            fields.sort();
-            DynamicConstant::Add(fields)
-        };
-
-    insert_dc(new_dc, dynamic_constants, reverse_map)
+    // If either there are no fields or the constant is non-zero, add it
+    if constant_val != 0 || fields.len() == 0 {
+        fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+    }
+
+    if fields.len() <= 1 {
+        // If there is only one term to add, just return it
+        fields[0]
+    } else {
+        fields.sort();
+        insert_dc(DynamicConstant::Add(fields), dynamic_constants, reverse_map)
+    }
 }
 
 pub fn dc_mul(
@@ -68,22 +80,20 @@ pub fn dc_mul(
         }
     }
 
-    let new_dc =
-        if fields.len() == 0 {
-            // If there are no non-constants, our result is a constant
-            DynamicConstant::Constant(constant_val)
-        } else if constant_val == 1 {
-            // If there are non-constants and our constant value is one, omit it
-            fields.sort();
-            DynamicConstant::Mul(fields)
-        } else {
-            // Both non-constant and constant pieces
-            fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
-            fields.sort();
-            DynamicConstant::Mul(fields)
-        };
-
-    insert_dc(new_dc, dynamic_constants, reverse_map)
+    if constant_val == 0 {
+        return insert_dc(DynamicConstant::Constant(0), dynamic_constants, reverse_map);
+    }
+
+    if constant_val != 1 || fields.len() == 0 {
+        fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+    }
+
+    if fields.len() <= 1 {
+        fields[0]
+    } else {
+        fields.sort();
+        insert_dc(DynamicConstant::Mul(fields), dynamic_constants, reverse_map)
+    }
 }
 
 pub fn dc_min(
@@ -108,17 +118,18 @@ pub fn dc_min(
         }
     }
 
-    let new_dc =
-        if let Some(const_val) = constant_val {
-            fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
-            fields.sort();
-            DynamicConstant::Min(fields)
-        } else {
-            fields.sort();
-            DynamicConstant::Min(fields)
-        };
+    if let Some(const_val) = constant_val {
+        fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+    }
+
+    assert!(fields.len() > 0, "Min of 0 dynamic constant expressions is undefined");
 
-    insert_dc(new_dc, dynamic_constants, reverse_map)
+    if fields.len() <= 1 {
+        fields[0]
+    } else {
+        fields.sort();
+        insert_dc(DynamicConstant::Min(fields), dynamic_constants, reverse_map)
+    }
 }
 
 pub fn dc_max(
@@ -143,17 +154,18 @@ pub fn dc_max(
         }
     }
 
-    let new_dc =
-        if let Some(const_val) = constant_val {
-            fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
-            fields.sort();
-            DynamicConstant::Max(fields)
-        } else {
-            fields.sort();
-            DynamicConstant::Max(fields)
-        };
+    if let Some(const_val) = constant_val {
+        fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+    }
 
-    insert_dc(new_dc, dynamic_constants, reverse_map)
+    assert!(fields.len() > 0, "Max of 0 dynamic constant expressions is undefined");
+
+    if fields.len() <= 1 {
+        fields[0]
+    } else {
+        fields.sort();
+        insert_dc(DynamicConstant::Max(fields), dynamic_constants, reverse_map)
+    }
 }
 
 pub fn dc_sub(
-- 
GitLab


From deebc46fbcd96ca0b03f361f059399d8bc3c29d7 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 15:59:31 -0600
Subject: [PATCH 04/16] Update builder to use dc normalization

---
 hercules_ir/src/build.rs | 55 ++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 19 deletions(-)

diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index 1dd326c3..bc57781d 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -70,17 +70,6 @@ impl<'a> Builder<'a> {
         }
     }
 
-    fn intern_dynamic_constant(&mut self, dyn_cons: DynamicConstant) -> DynamicConstantID {
-        if let Some(id) = self.interned_dynamic_constants.get(&dyn_cons) {
-            *id
-        } else {
-            let id = DynamicConstantID::new(self.interned_dynamic_constants.len());
-            self.interned_dynamic_constants.insert(dyn_cons.clone(), id);
-            self.module.dynamic_constants.push(dyn_cons);
-            id
-        }
-    }
-
     pub fn add_label(&mut self, label: &String) -> LabelID {
         if let Some(id) = self.interned_labels.get(label) {
             *id
@@ -406,11 +395,11 @@ impl<'a> Builder<'a> {
     }
 
     pub fn create_dynamic_constant_constant(&mut self, val: usize) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Constant(val))
+        dc_const(val, &mut self.module.dynamic_constants, &mut self.interned_dynamic_constants)
     }
 
-    pub fn create_dynamic_constant_parameter(&mut self, val: usize) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Parameter(val))
+    pub fn create_dynamic_constant_parameter(&mut self, idx: usize) -> DynamicConstantID {
+        dc_param(idx, &mut self.module.dynamic_constants, &mut self.interned_dynamic_constants)
     }
 
     pub fn create_dynamic_constant_add(
@@ -418,7 +407,18 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Add(x, y))
+        dc_add(vec![x, y],
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
+    }
+
+    pub fn create_dynamic_constant_add_many(
+        &mut self,
+        xs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        dc_add(xs,
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
     }
 
     pub fn create_dynamic_constant_sub(
@@ -426,7 +426,9 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Sub(x, y))
+        dc_sub(x, y,
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
     }
 
     pub fn create_dynamic_constant_mul(
@@ -434,7 +436,18 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Mul(x, y))
+        dc_mul(vec![x, y],
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
+    }
+
+    pub fn create_dynamic_constant_mul_many(
+        &mut self,
+        xs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        dc_mul(xs,
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
     }
 
     pub fn create_dynamic_constant_div(
@@ -442,7 +455,9 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Div(x, y))
+        dc_div(x, y,
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
     }
 
     pub fn create_dynamic_constant_rem(
@@ -450,7 +465,9 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        self.intern_dynamic_constant(DynamicConstant::Rem(x, y))
+        dc_rem(x, y,
+               &mut self.module.dynamic_constants,
+               &mut self.interned_dynamic_constants)
     }
 
     pub fn create_field_index(&self, idx: usize) -> Index {
-- 
GitLab


From 46537c8f4bed7c4090f97e8a7fe35d999d6f2aed Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 17:30:48 -0600
Subject: [PATCH 05/16] Update to normalization and implement in editor

---
 hercules_ir/src/build.rs            |  49 ++++++-----
 hercules_ir/src/dc_normalization.rs | 125 ++++++++++++++--------------
 hercules_opt/src/editor.rs          |  42 ++++++----
 3 files changed, 113 insertions(+), 103 deletions(-)

diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index bc57781d..b7844806 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -25,6 +25,23 @@ pub struct Builder<'a> {
     module: Module,
 }
 
+impl<'a> DynamicConstantView for Builder<'a> {
+    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+        &self.module.dynamic_constants[id.idx()]
+    }
+
+    fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID {
+        if let Some(id) = self.interned_dynamic_constants.get(&dc) {
+            *id
+        } else {
+            let id = DynamicConstantID::new(self.module.dynamic_constants.len());
+            self.module.dynamic_constants.push(dc.clone());
+            self.interned_dynamic_constants.insert(dc, id);
+            id
+        }
+    }
+}
+
 /*
  * Since the builder doesn't provide string names for nodes, we need a different
  * mechanism for allowing one to allocate node IDs before actually creating the
@@ -395,11 +412,11 @@ impl<'a> Builder<'a> {
     }
 
     pub fn create_dynamic_constant_constant(&mut self, val: usize) -> DynamicConstantID {
-        dc_const(val, &mut self.module.dynamic_constants, &mut self.interned_dynamic_constants)
+        dc_const(val, self)
     }
 
     pub fn create_dynamic_constant_parameter(&mut self, idx: usize) -> DynamicConstantID {
-        dc_param(idx, &mut self.module.dynamic_constants, &mut self.interned_dynamic_constants)
+        dc_param(idx, self)
     }
 
     pub fn create_dynamic_constant_add(
@@ -407,18 +424,14 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_add(vec![x, y],
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_add(vec![x, y], self)
     }
 
     pub fn create_dynamic_constant_add_many(
         &mut self,
         xs: Vec<DynamicConstantID>,
     ) -> DynamicConstantID {
-        dc_add(xs,
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_add(xs, self)
     }
 
     pub fn create_dynamic_constant_sub(
@@ -426,9 +439,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_sub(x, y,
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_sub(x, y, self)
     }
 
     pub fn create_dynamic_constant_mul(
@@ -436,18 +447,14 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_mul(vec![x, y],
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_mul(vec![x, y], self)
     }
 
     pub fn create_dynamic_constant_mul_many(
         &mut self,
         xs: Vec<DynamicConstantID>,
     ) -> DynamicConstantID {
-        dc_mul(xs,
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_mul(xs, self)
     }
 
     pub fn create_dynamic_constant_div(
@@ -455,9 +462,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_div(x, y,
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_div(x, y, self)
     }
 
     pub fn create_dynamic_constant_rem(
@@ -465,9 +470,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_rem(x, y,
-               &mut self.module.dynamic_constants,
-               &mut self.interned_dynamic_constants)
+        dc_rem(x, y, self)
     }
 
     pub fn create_field_index(&self, idx: usize) -> Index {
diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index 55effa3b..869ddbf8 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -3,47 +3,34 @@ use crate::*;
 use std::collections::HashMap;
 use std::cmp::{min, max};
 
-fn insert_dc(
-    dc: DynamicConstant,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
-) -> DynamicConstantID {
-    if let Some(id) = reverse_map.get(&dc) {
-        *id
-    } else {
-        let id = DynamicConstantID::new(dynamic_constants.len());
-        dynamic_constants.push(dc.clone());
-        reverse_map.insert(dc, id);
-        id
-    }
+pub trait DynamicConstantView {
+    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant;
+    fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID;
 }
 
-pub fn dc_const(
+pub fn dc_const<T: DynamicConstantView>(
     val: usize,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
-    insert_dc(DynamicConstant::Constant(val), dynamic_constants, reverse_map)
+    view.add_dynconst(DynamicConstant::Constant(val))
 }
 
-pub fn dc_param(
+pub fn dc_param<T: DynamicConstantView>(
     index: usize,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
-    insert_dc(DynamicConstant::Parameter(index), dynamic_constants, reverse_map)
+    view.add_dynconst(DynamicConstant::Parameter(index))
 }
 
-pub fn dc_add(
+pub fn dc_add<T: DynamicConstantView>(
     dcs: Vec<DynamicConstantID>,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
     let mut constant_val = 0;
     let mut fields = vec![];
 
     for dc in dcs {
-        match &dynamic_constants[dc.idx()] {
+        match view.get_dynconst(dc) {
             DynamicConstant::Constant(x) => constant_val += x,
             DynamicConstant::Add(xs) => fields.extend_from_slice(xs),
             _ => fields.push(dc),
@@ -52,7 +39,7 @@ pub fn dc_add(
 
     // If either there are no fields or the constant is non-zero, add it
     if constant_val != 0 || fields.len() == 0 {
-        fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+        fields.push(view.add_dynconst(DynamicConstant::Constant(constant_val)));
     }
 
     if fields.len() <= 1 {
@@ -60,20 +47,19 @@ pub fn dc_add(
         fields[0]
     } else {
         fields.sort();
-        insert_dc(DynamicConstant::Add(fields), dynamic_constants, reverse_map)
+        view.add_dynconst(DynamicConstant::Add(fields))
     }
 }
 
-pub fn dc_mul(
+pub fn dc_mul<T: DynamicConstantView>(
     dcs: Vec<DynamicConstantID>,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
     let mut constant_val = 1;
     let mut fields = vec![];
 
     for dc in dcs {
-        match &dynamic_constants[dc.idx()] {
+        match view.get_dynconst(dc) {
             DynamicConstant::Constant(x) => constant_val *= x,
             DynamicConstant::Mul(xs) => fields.extend_from_slice(xs),
             _ => fields.push(dc),
@@ -81,31 +67,30 @@ pub fn dc_mul(
     }
 
     if constant_val == 0 {
-        return insert_dc(DynamicConstant::Constant(0), dynamic_constants, reverse_map);
+        return view.add_dynconst(DynamicConstant::Constant(0));
     }
 
     if constant_val != 1 || fields.len() == 0 {
-        fields.push(insert_dc(DynamicConstant::Constant(constant_val), dynamic_constants, reverse_map));
+        fields.push(view.add_dynconst(DynamicConstant::Constant(constant_val)));
     }
 
     if fields.len() <= 1 {
         fields[0]
     } else {
         fields.sort();
-        insert_dc(DynamicConstant::Mul(fields), dynamic_constants, reverse_map)
+        view.add_dynconst(DynamicConstant::Mul(fields))
     }
 }
 
-pub fn dc_min(
+pub fn dc_min<T: DynamicConstantView>(
     dcs: Vec<DynamicConstantID>,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
     let mut constant_val : Option<usize> = None;
     let mut fields = vec![];
 
     for dc in dcs {
-        match &dynamic_constants[dc.idx()] {
+        match view.get_dynconst(dc) {
             DynamicConstant::Constant(x) => {
                 if let Some(cur_min) = constant_val {
                     constant_val = Some(min(cur_min, *x));
@@ -119,7 +104,7 @@ pub fn dc_min(
     }
 
     if let Some(const_val) = constant_val {
-        fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+        fields.push(view.add_dynconst(DynamicConstant::Constant(const_val)));
     }
 
     assert!(fields.len() > 0, "Min of 0 dynamic constant expressions is undefined");
@@ -128,20 +113,19 @@ pub fn dc_min(
         fields[0]
     } else {
         fields.sort();
-        insert_dc(DynamicConstant::Min(fields), dynamic_constants, reverse_map)
+        view.add_dynconst(DynamicConstant::Min(fields))
     }
 }
 
-pub fn dc_max(
+pub fn dc_max<T: DynamicConstantView>(
     dcs: Vec<DynamicConstantID>,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
     let mut constant_val : Option<usize> = None;
     let mut fields = vec![];
 
     for dc in dcs {
-        match &dynamic_constants[dc.idx()] {
+        match view.get_dynconst(dc) {
             DynamicConstant::Constant(x) => {
                 if let Some(cur_max) = constant_val {
                     constant_val = Some(max(cur_max, *x));
@@ -155,7 +139,7 @@ pub fn dc_max(
     }
 
     if let Some(const_val) = constant_val {
-        fields.push(insert_dc(DynamicConstant::Constant(const_val), dynamic_constants, reverse_map));
+        fields.push(view.add_dynconst(DynamicConstant::Constant(const_val)));
     }
 
     assert!(fields.len() > 0, "Max of 0 dynamic constant expressions is undefined");
@@ -164,47 +148,60 @@ pub fn dc_max(
         fields[0]
     } else {
         fields.sort();
-        insert_dc(DynamicConstant::Max(fields), dynamic_constants, reverse_map)
+        view.add_dynconst(DynamicConstant::Max(fields))
     }
 }
 
-pub fn dc_sub(
+pub fn dc_sub<T: DynamicConstantView>(
     x: DynamicConstantID,
     y: DynamicConstantID,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
-    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+    match (view.get_dynconst(x), view.get_dynconst(y)) {
         (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            insert_dc(DynamicConstant::Constant(x - y), dynamic_constants, reverse_map),
+            view.add_dynconst(DynamicConstant::Constant(x - y)),
         (_, DynamicConstant::Constant(0)) => x,
-        _ => insert_dc(DynamicConstant::Sub(x, y), dynamic_constants, reverse_map),
+        _ => view.add_dynconst(DynamicConstant::Sub(x, y)),
     }
 }
 
-pub fn dc_div(
+pub fn dc_div<T: DynamicConstantView>(
     x: DynamicConstantID,
     y: DynamicConstantID,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
-    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+    match (view.get_dynconst(x), view.get_dynconst(y)) {
         (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            insert_dc(DynamicConstant::Constant(x / y), dynamic_constants, reverse_map),
+            view.add_dynconst(DynamicConstant::Constant(x / y)),
         (_, DynamicConstant::Constant(1)) => x,
-        _ => insert_dc(DynamicConstant::Div(x, y), dynamic_constants, reverse_map),
+        _ => view.add_dynconst(DynamicConstant::Div(x, y)),
     }
 }
 
-pub fn dc_rem(
+pub fn dc_rem<T: DynamicConstantView>(
     x: DynamicConstantID,
     y: DynamicConstantID,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    view: &mut T,
 ) -> DynamicConstantID {
-    match (&dynamic_constants[x.idx()], &dynamic_constants[y.idx()]) {
+    match (view.get_dynconst(x), view.get_dynconst(y)) {
         (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            insert_dc(DynamicConstant::Constant(x % y), dynamic_constants, reverse_map),
-        _ => insert_dc(DynamicConstant::Rem(x, y), dynamic_constants, reverse_map),
+            view.add_dynconst(DynamicConstant::Constant(x % y)),
+        _ => view.add_dynconst(DynamicConstant::Rem(x, y)),
+    }
+}
+
+pub fn dc_normalize<T: DynamicConstantView>(
+    dc: DynamicConstant,
+    view: &mut T,
+) -> DynamicConstantID {
+    match dc {
+        DynamicConstant::Add(xs) => dc_add(xs, view),
+        DynamicConstant::Mul(xs) => dc_mul(xs, view),
+        DynamicConstant::Min(xs) => dc_min(xs, view),
+        DynamicConstant::Max(xs) => dc_max(xs, view),
+        DynamicConstant::Sub(x, y) => dc_sub(x, y, view),
+        DynamicConstant::Div(x, y) => dc_div(x, y, view),
+        DynamicConstant::Rem(x, y) => dc_rem(x, y, view),
+        _ => view.add_dynconst(dc),
     }
 }
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 8b90710e..8164ff9d 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -725,22 +725,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
     }
 
     pub fn add_dynamic_constant(&mut self, dynamic_constant: DynamicConstant) -> DynamicConstantID {
-        let pos = self
-            .editor
-            .dynamic_constants
-            .borrow()
-            .iter()
-            .chain(self.added_dynamic_constants.iter())
-            .position(|c| *c == dynamic_constant);
-        if let Some(idx) = pos {
-            DynamicConstantID::new(idx)
-        } else {
-            let id = DynamicConstantID::new(
-                self.editor.dynamic_constants.borrow().len() + self.added_dynamic_constants.len(),
-            );
-            self.added_dynamic_constants.push(dynamic_constant);
-            id
-        }
+        dc_normalize(dynamic_constant, self)
     }
 
     pub fn get_dynamic_constant(
@@ -770,6 +755,31 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
     }
 }
 
+impl<'a, 'b> DynamicConstantView for FunctionEdit<'a, 'b> {
+    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+        self.get_dynamic_constant(id)
+    }
+
+    fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID {
+        let pos = self
+            .editor
+            .dynamic_constants
+            .borrow()
+            .iter()
+            .chain(self.added_dynamic_constants.iter())
+            .position(|c| *c == dc);
+        if let Some(idx) = pos {
+            DynamicConstantID::new(idx)
+        } else {
+            let id = DynamicConstantID::new(
+                self.editor.dynamic_constants.borrow().len() + self.added_dynamic_constants.len(),
+            );
+            self.added_dynamic_constants.push(dc);
+            id
+        }
+    }
+}
+
 #[cfg(test)]
 mod editor_tests {
     #[allow(unused_imports)]
-- 
GitLab


From c547e93db5aa3e5a959bf4b103e8c2c458d100c3 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 17:37:01 -0600
Subject: [PATCH 06/16] Normalize in parser

---
 hercules_ir/src/dc_normalization.rs |  1 -
 hercules_ir/src/parse.rs            | 32 ++++++++++++++++++-----------
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index 869ddbf8..4705eac3 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -1,6 +1,5 @@
 use crate::*;
 
-use std::collections::HashMap;
 use std::cmp::{min, max};
 
 pub trait DynamicConstantView {
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index cdad54f9..1f6d931e 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -87,16 +87,7 @@ impl<'a> Context<'a> {
     }
 
     fn get_dynamic_constant_id(&mut self, dynamic_constant: DynamicConstant) -> DynamicConstantID {
-        if let Some(id) = self.interned_dynamic_constants.get(&dynamic_constant) {
-            *id
-        } else {
-            let id = DynamicConstantID::new(self.interned_dynamic_constants.len());
-            self.interned_dynamic_constants
-                .insert(dynamic_constant.clone(), id);
-            self.reverse_dynamic_constant_map
-                .insert(id, dynamic_constant);
-            id
-        }
+        dc_normalize(dynamic_constant, self)
     }
 
     fn get_label_id(&mut self, label: String) -> LabelID {
@@ -110,6 +101,23 @@ impl<'a> Context<'a> {
     }
 }
 
+impl<'a> DynamicConstantView for Context<'a> {
+    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+        &self.reverse_dynamic_constant_map[&id]
+    }
+
+    fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID {
+        if let Some(id) = self.interned_dynamic_constants.get(&dc) {
+            *id
+        } else {
+            let id = DynamicConstantID::new(self.reverse_dynamic_constant_map.len());
+            self.interned_dynamic_constants.insert(dc.clone(), id);
+            self.reverse_dynamic_constant_map.insert(id, dc);
+            id
+        }
+    }
+}
+
 /*
  * A module is just a file with a list of functions.
  */
@@ -946,9 +954,9 @@ fn parse_dynamic_constant<'a>(
                 ),
             )),
             |(op, (x, y))| match op {
-                '+' => DynamicConstant::Add(x, y),
+                '+' => DynamicConstant::Add(vec![x, y]),
                 '-' => DynamicConstant::Sub(x, y),
-                '*' => DynamicConstant::Mul(x, y),
+                '*' => DynamicConstant::Mul(vec![x, y]),
                 '/' => DynamicConstant::Div(x, y),
                 '%' => DynamicConstant::Rem(x, y),
                 _ => panic!("Invalid parse"),
-- 
GitLab


From e45a5aaf0b1e5933c13d0848b32d72dff307c44f Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Wed, 29 Jan 2025 18:03:46 -0600
Subject: [PATCH 07/16] Handle normalization in type-checker

---
 hercules_ir/src/typecheck.rs | 424 +++++++++++------------------------
 1 file changed, 129 insertions(+), 295 deletions(-)

diff --git a/hercules_ir/src/typecheck.rs b/hercules_ir/src/typecheck.rs
index a80dd422..698223b4 100644
--- a/hercules_ir/src/typecheck.rs
+++ b/hercules_ir/src/typecheck.rs
@@ -1,4 +1,3 @@
-use std::cmp::{max, min};
 use std::collections::HashMap;
 use std::iter::zip;
 
@@ -184,18 +183,20 @@ fn typeflow(
         dynamic_constants: &Vec<DynamicConstant>,
         num_parameters: u32,
     ) -> bool {
-        match dynamic_constants[root.idx()] {
+        match &dynamic_constants[root.idx()] {
             DynamicConstant::Constant(_) => true,
-            DynamicConstant::Parameter(idx) => idx < num_parameters as usize,
-            DynamicConstant::Add(x, y)
-            | DynamicConstant::Sub(x, y)
-            | DynamicConstant::Mul(x, y)
+            DynamicConstant::Parameter(idx) => *idx < num_parameters as usize,
+            DynamicConstant::Add(xs)
+            | DynamicConstant::Mul(xs)
+            | DynamicConstant::Min(xs)
+            | DynamicConstant::Max(xs) => {
+                xs.iter().all(|dc| check_dynamic_constants(*dc, dynamic_constants, num_parameters))
+            }
+            DynamicConstant::Sub(x, y)
             | DynamicConstant::Div(x, y)
-            | DynamicConstant::Rem(x, y)
-            | DynamicConstant::Min(x, y)
-            | DynamicConstant::Max(x, y) => {
-                check_dynamic_constants(x, dynamic_constants, num_parameters)
-                    && check_dynamic_constants(y, dynamic_constants, num_parameters)
+            | DynamicConstant::Rem(x, y) => {
+                check_dynamic_constants(*x, dynamic_constants, num_parameters)
+                    && check_dynamic_constants(*y, dynamic_constants, num_parameters)
             }
         }
     }
@@ -733,10 +734,14 @@ fn typeflow(
                 }
             }
 
+            // Construct the substitution object
+            let mut subst = DCSubst::new(types, reverse_type_map, dynamic_constants, reverse_dynamic_constant_map, dc_args);
+
             // Check argument types.
             for (input, param_ty) in zip(inputs.iter().skip(1), callee.param_types.iter()) {
+                let param_ty = subst.type_subst(*param_ty);
                 if let Concrete(input_id) = input {
-                    if !types_match(types, dynamic_constants, dc_args, *param_ty, *input_id) {
+                    if param_ty != *input_id {
                         return Error(String::from(
                             "Call node mismatches argument types with callee function.",
                         ));
@@ -747,14 +752,7 @@ fn typeflow(
                 }
             }
 
-            Concrete(type_subst(
-                types,
-                dynamic_constants,
-                reverse_type_map,
-                reverse_dynamic_constant_map,
-                dc_args,
-                callee.return_type,
-            ))
+            Concrete(subst.type_subst(callee.return_type))
         }
         Node::IntrinsicCall { intrinsic, args: _ } => {
             let num_params = match intrinsic {
@@ -1071,307 +1069,143 @@ pub fn cast_compatible(src_ty: &Type, dst_ty: &Type) -> bool {
 }
 
 /*
- * Determine if the given type matches the parameter type when the provided
- * dynamic constants are substituted in for the dynamic constants used in the
- * parameter type.
+ * Data structures and methods for substituting given dynamic constant arguments into the provided
+ * types and dynamic constants
  */
-fn types_match(
-    types: &Vec<Type>,
-    dynamic_constants: &Vec<DynamicConstant>,
-    dc_args: &Box<[DynamicConstantID]>,
-    param: TypeID,
-    input: TypeID,
-) -> bool {
-    // Note that we can't just check whether the type ids are equal since them
-    // being equal does not mean they match when we properly substitute in the
-    // dynamic constant arguments
-
-    match (&types[param.idx()], &types[input.idx()]) {
-        (Type::Control, Type::Control)
-        | (Type::Boolean, Type::Boolean)
-        | (Type::Integer8, Type::Integer8)
-        | (Type::Integer16, Type::Integer16)
-        | (Type::Integer32, Type::Integer32)
-        | (Type::Integer64, Type::Integer64)
-        | (Type::UnsignedInteger8, Type::UnsignedInteger8)
-        | (Type::UnsignedInteger16, Type::UnsignedInteger16)
-        | (Type::UnsignedInteger32, Type::UnsignedInteger32)
-        | (Type::UnsignedInteger64, Type::UnsignedInteger64)
-        | (Type::Float32, Type::Float32)
-        | (Type::Float64, Type::Float64) => true,
-        (Type::Product(ps), Type::Product(is)) | (Type::Summation(ps), Type::Summation(is)) => {
-            ps.len() == is.len()
-                && ps
-                    .iter()
-                    .zip(is.iter())
-                    .all(|(p, i)| types_match(types, dynamic_constants, dc_args, *p, *i))
-        }
-        (Type::Array(p, pds), Type::Array(i, ids)) => {
-            types_match(types, dynamic_constants, dc_args, *p, *i)
-                && pds.len() == ids.len()
-                && pds
-                    .iter()
-                    .zip(ids.iter())
-                    .all(|(pd, id)| dyn_consts_match(dynamic_constants, dc_args, *pd, *id))
-        }
-        (_, _) => false,
-    }
+struct DCSubst<'a> {
+    types: &'a mut Vec<Type>,
+    reverse_type_map: &'a mut HashMap<Type, TypeID>,
+    dynamic_constants: &'a mut Vec<DynamicConstant>,
+    reverse_dynamic_constant_map: &'a mut HashMap<DynamicConstant, DynamicConstantID>,
+    dc_args: &'a [DynamicConstantID],
 }
 
-/*
- * Determine if the given dynamic constant matches the parameter's dynamic
- * constants when the provided dynamic constants are substituted in for the
- * dynamic constants used in the parameter's dynamic constant. Implement dynamic
- * constant normalization here as well - i.e., 1 * 2 * 3 = 6.
- */
-fn dyn_consts_match(
-    dynamic_constants: &Vec<DynamicConstant>,
-    dc_args: &Box<[DynamicConstantID]>,
-    left: DynamicConstantID,
-    right: DynamicConstantID,
-) -> bool {
-    // First, try evaluating the DCs and seeing if they're the same value.
-    if let (Some(cons1), Some(cons2)) = (
-        evaluate_dynamic_constant(left, dynamic_constants),
-        evaluate_dynamic_constant(right, dynamic_constants),
-    ) {
-        return cons1 == cons2;
+impl<'a> DynamicConstantView for DCSubst<'a> {
+    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+        &self.dynamic_constants[id.idx()]
     }
 
-    match (
-        &dynamic_constants[left.idx()],
-        &dynamic_constants[right.idx()],
-    ) {
-        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) => x == y,
-        (DynamicConstant::Parameter(l), DynamicConstant::Parameter(r)) => l == r,
-        (DynamicConstant::Parameter(i), _) => dyn_consts_match(
-            dynamic_constants,
-            dc_args,
-            min(right, dc_args[*i]),
-            max(right, dc_args[*i]),
-        ),
-        (_, DynamicConstant::Parameter(i)) => dyn_consts_match(
+    fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID {
+        if let Some(id) = self.reverse_dynamic_constant_map.get(&dc) {
+            *id
+        } else {
+            let id = DynamicConstantID::new(self.dynamic_constants.len());
+            self.reverse_dynamic_constant_map.insert(dc.clone(), id);
+            self.dynamic_constants.push(dc);
+            id
+        }
+    }
+}
+
+impl<'a> DCSubst<'a> {
+    fn new(
+        types: &'a mut Vec<Type>,
+        reverse_type_map: &'a mut HashMap<Type, TypeID>,
+        dynamic_constants: &'a mut Vec<DynamicConstant>,
+        reverse_dynamic_constant_map: &'a mut HashMap<DynamicConstant, DynamicConstantID>,
+        dc_args: &'a [DynamicConstantID],
+    ) -> Self {
+        Self {
+            types,
+            reverse_type_map,
             dynamic_constants,
+            reverse_dynamic_constant_map,
             dc_args,
-            min(left, dc_args[*i]),
-            max(left, dc_args[*i]),
-        ),
-        (DynamicConstant::Add(ll, lr), DynamicConstant::Add(rl, rr))
-        | (DynamicConstant::Mul(ll, lr), DynamicConstant::Mul(rl, rr))
-        | (DynamicConstant::Min(ll, lr), DynamicConstant::Min(rl, rr))
-        | (DynamicConstant::Max(ll, lr), DynamicConstant::Max(rl, rr)) => {
-            // Normalize for associative ops by always looking at smaller DC ID
-            // as left arm and larger DC ID as right arm.
-            dyn_consts_match(dynamic_constants, dc_args, min(*ll, *lr), min(*rl, *rr))
-                && dyn_consts_match(dynamic_constants, dc_args, max(*ll, *lr), max(*rl, *rr))
         }
-        (DynamicConstant::Sub(ll, lr), DynamicConstant::Sub(rl, rr))
-        | (DynamicConstant::Div(ll, lr), DynamicConstant::Div(rl, rr))
-        | (DynamicConstant::Rem(ll, lr), DynamicConstant::Rem(rl, rr)) => {
-            dyn_consts_match(dynamic_constants, dc_args, *ll, *rl)
-                && dyn_consts_match(dynamic_constants, dc_args, *lr, *rr)
-        }
-        (_, _) => false,
     }
-}
 
-/*
- * Substitutes the given dynamic constant arguments into the provided type and
- * returns the appropriate typeID (potentially creating new types and dynamic
- * constants in the process)
- */
-fn type_subst(
-    types: &mut Vec<Type>,
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_type_map: &mut HashMap<Type, TypeID>,
-    reverse_dynamic_constant_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
-    dc_args: &Box<[DynamicConstantID]>,
-    typ: TypeID,
-) -> TypeID {
     fn intern_type(
+        &mut self,
         ty: Type,
-        types: &mut Vec<Type>,
-        reverse_type_map: &mut HashMap<Type, TypeID>,
     ) -> TypeID {
-        if let Some(id) = reverse_type_map.get(&ty) {
+        if let Some(id) = self.reverse_type_map.get(&ty) {
             *id
         } else {
-            let id = TypeID::new(types.len());
-            reverse_type_map.insert(ty.clone(), id);
-            types.push(ty);
+            let id = TypeID::new(self.types.len());
+            self.reverse_type_map.insert(ty.clone(), id);
+            self.types.push(ty);
             id
         }
     }
 
-    match &types[typ.idx()] {
-        Type::Control
-        | Type::Boolean
-        | Type::Integer8
-        | Type::Integer16
-        | Type::Integer32
-        | Type::Integer64
-        | Type::UnsignedInteger8
-        | Type::UnsignedInteger16
-        | Type::UnsignedInteger32
-        | Type::UnsignedInteger64
-        | Type::Float32
-        | Type::Float64 => typ,
-        Type::Product(ts) => {
-            let mut new_ts = vec![];
-            for t in ts.clone().iter() {
-                new_ts.push(type_subst(
-                    types,
-                    dynamic_constants,
-                    reverse_type_map,
-                    reverse_dynamic_constant_map,
-                    dc_args,
-                    *t,
-                ));
+    fn type_subst(
+        &mut self,
+        typ: TypeID,
+    ) -> TypeID {
+        match &self.types[typ.idx()] {
+            Type::Control
+            | Type::Boolean
+            | Type::Integer8
+            | Type::Integer16
+            | Type::Integer32
+            | Type::Integer64
+            | Type::UnsignedInteger8
+            | Type::UnsignedInteger16
+            | Type::UnsignedInteger32
+            | Type::UnsignedInteger64
+            | Type::Float32
+            | Type::Float64 => typ,
+            Type::Product(ts) => {
+                let new_ts = ts.clone().iter().map(|t| self.type_subst(*t)).collect();
+                self.intern_type(Type::Product(new_ts))
             }
-            intern_type(Type::Product(new_ts.into()), types, reverse_type_map)
-        }
-        Type::Summation(ts) => {
-            let mut new_ts = vec![];
-            for t in ts.clone().iter() {
-                new_ts.push(type_subst(
-                    types,
-                    dynamic_constants,
-                    reverse_type_map,
-                    reverse_dynamic_constant_map,
-                    dc_args,
-                    *t,
-                ));
+            Type::Summation(ts) => {
+                let new_ts = ts.clone().iter().map(|t| self.type_subst(*t)).collect();
+                self.intern_type(Type::Summation(new_ts))
             }
-            intern_type(Type::Summation(new_ts.into()), types, reverse_type_map)
-        }
-        Type::Array(elem, dims) => {
-            let ds = dims.clone();
-            let new_elem = type_subst(
-                types,
-                dynamic_constants,
-                reverse_type_map,
-                reverse_dynamic_constant_map,
-                dc_args,
-                *elem,
-            );
-            let mut new_dims = vec![];
-            for d in ds.iter() {
-                new_dims.push(dyn_const_subst(
-                    dynamic_constants,
-                    reverse_dynamic_constant_map,
-                    dc_args,
-                    *d,
-                ));
+            Type::Array(elem, dims) => {
+                let elem = *elem;
+                let new_dims = dims.clone().iter().map(|d| self.dyn_const_subst(*d)).collect();
+                let new_elem = self.type_subst(elem);
+                self.intern_type(Type::Array(new_elem, new_dims))
             }
-            intern_type(
-                Type::Array(new_elem, new_dims.into()),
-                types,
-                reverse_type_map,
-            )
         }
     }
-}
 
-fn dyn_const_subst(
-    dynamic_constants: &mut Vec<DynamicConstant>,
-    reverse_dynamic_constant_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
-    dc_args: &Box<[DynamicConstantID]>,
-    dyn_const: DynamicConstantID,
-) -> DynamicConstantID {
-    fn intern_dyn_const(
-        dc: DynamicConstant,
-        dynamic_constants: &mut Vec<DynamicConstant>,
-        reverse_dynamic_constant_map: &mut HashMap<DynamicConstant, DynamicConstantID>,
+    fn dyn_const_subst(
+        &mut self,
+        dyn_const: DynamicConstantID,
     ) -> DynamicConstantID {
-        if let Some(id) = reverse_dynamic_constant_map.get(&dc) {
-            *id
-        } else {
-            let id = DynamicConstantID::new(dynamic_constants.len());
-            reverse_dynamic_constant_map.insert(dc.clone(), id);
-            dynamic_constants.push(dc);
-            id
-        }
-    }
-
-    match &dynamic_constants[dyn_const.idx()] {
-        DynamicConstant::Constant(_) => dyn_const,
-        DynamicConstant::Parameter(i) => dc_args[*i],
-        DynamicConstant::Add(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Add(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Sub(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Sub(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Mul(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Mul(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Div(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Div(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Rem(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Rem(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Min(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Min(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
-        }
-        DynamicConstant::Max(l, r) => {
-            let x = *l;
-            let y = *r;
-            let sx = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, x);
-            let sy = dyn_const_subst(dynamic_constants, reverse_dynamic_constant_map, dc_args, y);
-            intern_dyn_const(
-                DynamicConstant::Max(sx, sy),
-                dynamic_constants,
-                reverse_dynamic_constant_map,
-            )
+        match &self.dynamic_constants[dyn_const.idx()] {
+            DynamicConstant::Constant(_) => dyn_const,
+            DynamicConstant::Parameter(i) => self.dc_args[*i],
+            DynamicConstant::Add(xs) => {
+                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                dc_add(sxs, self)
+            }
+            DynamicConstant::Sub(l, r) => {
+                let x = *l;
+                let y = *r;
+                let sx = self.dyn_const_subst(x);
+                let sy = self.dyn_const_subst(y);
+                dc_sub(sx, sy, self)
+            }
+            DynamicConstant::Mul(xs) => {
+                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                dc_mul(sxs, self)
+            }
+            DynamicConstant::Div(l, r) => {
+                let x = *l;
+                let y = *r;
+                let sx = self.dyn_const_subst(x);
+                let sy = self.dyn_const_subst(y);
+                dc_div(sx, sy, self)
+            }
+            DynamicConstant::Rem(l, r) => {
+                let x = *l;
+                let y = *r;
+                let sx = self.dyn_const_subst(x);
+                let sy = self.dyn_const_subst(y);
+                dc_rem(sx, sy, self)
+            }
+            DynamicConstant::Min(xs) => {
+                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                dc_min(sxs, self)
+            }
+            DynamicConstant::Max(xs) => {
+                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                dc_max(sxs, self)
+            }
         }
     }
 }
-- 
GitLab


From c156579b16eadcf46123ab795aa7f6e24a2b7a89 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 10:27:37 -0600
Subject: [PATCH 08/16] Handle n-ary dynamic constants in cpu back-end

---
 hercules_cg/src/cpu.rs | 108 +++++++++++++++++++++++++++++------------
 1 file changed, 78 insertions(+), 30 deletions(-)

diff --git a/hercules_cg/src/cpu.rs b/hercules_cg/src/cpu.rs
index 3750c4f6..94b8cc5d 100644
--- a/hercules_cg/src/cpu.rs
+++ b/hercules_cg/src/cpu.rs
@@ -562,12 +562,12 @@ impl<'a> CPUContext<'a> {
     ) -> Result<(), Error> {
         let body = &mut block.body;
         for dc in dynamic_constants_bottom_up(&self.dynamic_constants) {
-            match self.dynamic_constants[dc.idx()] {
+            match &self.dynamic_constants[dc.idx()] {
                 DynamicConstant::Constant(val) => {
                     write!(body, "  %dc{} = bitcast i64 {} to i64\n", dc.idx(), val)?
                 }
                 DynamicConstant::Parameter(idx) => {
-                    if idx < num_dc_params as usize {
+                    if *idx < num_dc_params as usize {
                         write!(
                             body,
                             "  %dc{} = bitcast i64 %dc_p{} to i64\n",
@@ -578,13 +578,25 @@ impl<'a> CPUContext<'a> {
                         write!(body, "  %dc{} = bitcast i64 0 to i64\n", dc.idx())?
                     }
                 }
-                DynamicConstant::Add(left, right) => write!(
-                    body,
-                    "  %dc{} = add i64%dc{},%dc{}\n",
-                    dc.idx(),
-                    left.idx(),
-                    right.idx()
-                )?,
+                DynamicConstant::Add(xs) => {
+                    let mut xs = xs.iter().peekable();
+                    let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
+                    let mut idx = 0;
+                    while let Some(x) = xs.next() {
+                        let new_val = format!("%dc{}{}", dc.idx(),
+                            if xs.peek().is_some() { format!(".{}", idx) }
+                            else { "".to_string() });
+                        write!(
+                            body,
+                            "  {} = add i64{},%dc{}\n",
+                            new_val,
+                            cur_value,
+                            x.idx()
+                        )?;
+                        cur_value = new_val;
+                        idx += 1;
+                    }
+                }
                 DynamicConstant::Sub(left, right) => write!(
                     body,
                     "  %dc{} = sub i64%dc{},%dc{}\n",
@@ -592,13 +604,25 @@ impl<'a> CPUContext<'a> {
                     left.idx(),
                     right.idx()
                 )?,
-                DynamicConstant::Mul(left, right) => write!(
-                    body,
-                    "  %dc{} = mul i64%dc{},%dc{}\n",
-                    dc.idx(),
-                    left.idx(),
-                    right.idx()
-                )?,
+                DynamicConstant::Mul(xs) => {
+                    let mut xs = xs.iter().peekable();
+                    let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
+                    let mut idx = 0;
+                    while let Some(x) = xs.next() {
+                        let new_val = format!("%dc{}{}", dc.idx(),
+                            if xs.peek().is_some() { format!(".{}", idx) }
+                            else { "".to_string() });
+                        write!(
+                            body,
+                            "  {} = mul i64{},%dc{}\n",
+                            new_val,
+                            cur_value,
+                            x.idx()
+                        )?;
+                        cur_value = new_val;
+                        idx += 1;
+                    }
+                }
                 DynamicConstant::Div(left, right) => write!(
                     body,
                     "  %dc{} = udiv i64%dc{},%dc{}\n",
@@ -613,20 +637,44 @@ impl<'a> CPUContext<'a> {
                     left.idx(),
                     right.idx()
                 )?,
-                DynamicConstant::Min(left, right) => write!(
-                    body,
-                    "  %dc{} = call @llvm.umin.i64(i64%dc{},i64%dc{})\n",
-                    dc.idx(),
-                    left.idx(),
-                    right.idx()
-                )?,
-                DynamicConstant::Max(left, right) => write!(
-                    body,
-                    "  %dc{} = call @llvm.umax.i64(i64%dc{},i64%dc{})\n",
-                    dc.idx(),
-                    left.idx(),
-                    right.idx()
-                )?,
+                DynamicConstant::Min(xs) => {
+                    let mut xs = xs.iter().peekable();
+                    let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
+                    let mut idx = 0;
+                    while let Some(x) = xs.next() {
+                        let new_val = format!("%dc{}{}", dc.idx(),
+                            if xs.peek().is_some() { format!(".{}", idx) }
+                            else { "".to_string() });
+                        write!(
+                            body,
+                            "  {} = call @llvm.umin.i64(i64{},i64%dc{}))\n",
+                            new_val,
+                            cur_value,
+                            x.idx()
+                        )?;
+                        cur_value = new_val;
+                        idx += 1;
+                    }
+                }
+                DynamicConstant::Max(xs) => {
+                    let mut xs = xs.iter().peekable();
+                    let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
+                    let mut idx = 0;
+                    while let Some(x) = xs.next() {
+                        let new_val = format!("%dc{}{}", dc.idx(),
+                            if xs.peek().is_some() { format!(".{}", idx) }
+                            else { "".to_string() });
+                        write!(
+                            body,
+                            "  {} = call @llvm.umax.i64(i64{},i64%dc{}))\n",
+                            new_val,
+                            cur_value,
+                            x.idx()
+                        )?;
+                        cur_value = new_val;
+                        idx += 1;
+                    }
+                }
             }
         }
         Ok(())
-- 
GitLab


From f826fd64b6375af5690d639bf960606d41f4b44f Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 10:37:12 -0600
Subject: [PATCH 09/16] Handle n-ary dynamic constants in rt back-end

---
 hercules_cg/src/rt.rs | 86 +++++++++++++++++++++++++++++--------------
 1 file changed, 59 insertions(+), 27 deletions(-)

diff --git a/hercules_cg/src/rt.rs b/hercules_cg/src/rt.rs
index 1bfc01fe..ddf892fc 100644
--- a/hercules_cg/src/rt.rs
+++ b/hercules_cg/src/rt.rs
@@ -441,57 +441,89 @@ impl<'a> RTContext<'a> {
         id: DynamicConstantID,
         w: &mut W,
     ) -> Result<(), Error> {
-        match self.module.dynamic_constants[id.idx()] {
+        match &self.module.dynamic_constants[id.idx()] {
             DynamicConstant::Constant(val) => write!(w, "{}", val)?,
             DynamicConstant::Parameter(idx) => write!(w, "dc_p{}", idx)?,
-            DynamicConstant::Add(left, right) => {
+            DynamicConstant::Add(xs) => {
                 write!(w, "(")?;
-                self.codegen_dynamic_constant(left, w)?;
-                write!(w, "+")?;
-                self.codegen_dynamic_constant(right, w)?;
+                let mut xs = xs.iter();
+                self.codegen_dynamic_constant(*xs.next().unwrap(), w)?;
+                for x in xs {
+                    write!(w, "+")?;
+                    self.codegen_dynamic_constant(*x, w)?;
+                }
                 write!(w, ")")?;
             }
             DynamicConstant::Sub(left, right) => {
                 write!(w, "(")?;
-                self.codegen_dynamic_constant(left, w)?;
+                self.codegen_dynamic_constant(*left, w)?;
                 write!(w, "-")?;
-                self.codegen_dynamic_constant(right, w)?;
+                self.codegen_dynamic_constant(*right, w)?;
                 write!(w, ")")?;
             }
-            DynamicConstant::Mul(left, right) => {
+            DynamicConstant::Mul(xs) => {
                 write!(w, "(")?;
-                self.codegen_dynamic_constant(left, w)?;
-                write!(w, "*")?;
-                self.codegen_dynamic_constant(right, w)?;
+                let mut xs = xs.iter();
+                self.codegen_dynamic_constant(*xs.next().unwrap(), w)?;
+                for x in xs {
+                    write!(w, "*")?;
+                    self.codegen_dynamic_constant(*x, w)?;
+                }
                 write!(w, ")")?;
             }
             DynamicConstant::Div(left, right) => {
                 write!(w, "(")?;
-                self.codegen_dynamic_constant(left, w)?;
+                self.codegen_dynamic_constant(*left, w)?;
                 write!(w, "/")?;
-                self.codegen_dynamic_constant(right, w)?;
+                self.codegen_dynamic_constant(*right, w)?;
                 write!(w, ")")?;
             }
             DynamicConstant::Rem(left, right) => {
                 write!(w, "(")?;
-                self.codegen_dynamic_constant(left, w)?;
+                self.codegen_dynamic_constant(*left, w)?;
                 write!(w, "%")?;
-                self.codegen_dynamic_constant(right, w)?;
+                self.codegen_dynamic_constant(*right, w)?;
                 write!(w, ")")?;
             }
-            DynamicConstant::Min(left, right) => {
-                write!(w, "::core::cmp::min(")?;
-                self.codegen_dynamic_constant(left, w)?;
-                write!(w, ",")?;
-                self.codegen_dynamic_constant(right, w)?;
-                write!(w, ")")?;
+            DynamicConstant::Min(xs) => {
+                let mut xs = xs.iter().peekable();
+
+                // Track the number of parentheses we open that need to be closed later
+                let mut opens = 0;
+                while let Some(x) = xs.next() {
+                    if xs.peek().is_none() {
+                        // For the last element, we just print it
+                        self.codegen_dynamic_constant(*x, w)?;
+                    } else {
+                        // Otherwise, we create a new call to min and print the element as the
+                        // first argument
+                        write!(w, "::core::cmp::min(")?;
+                        self.codegen_dynamic_constant(*x, w)?;
+                        write!(w, ",")?;
+                        opens += 1;
+                    }
+                }
+                for _ in 0..opens {
+                    write!(w, ")")?;
+                }
             }
-            DynamicConstant::Max(left, right) => {
-                write!(w, "::core::cmp::max(")?;
-                self.codegen_dynamic_constant(left, w)?;
-                write!(w, ",")?;
-                self.codegen_dynamic_constant(right, w)?;
-                write!(w, ")")?;
+            DynamicConstant::Max(xs) => {
+                let mut xs = xs.iter().peekable();
+
+                let mut opens = 0;
+                while let Some(x) = xs.next() {
+                    if xs.peek().is_none() {
+                        self.codegen_dynamic_constant(*x, w)?;
+                    } else {
+                        write!(w, "::core::cmp::max(")?;
+                        self.codegen_dynamic_constant(*x, w)?;
+                        write!(w, ",")?;
+                        opens += 1;
+                    }
+                }
+                for _ in 0..opens {
+                    write!(w, ")")?;
+                }
             }
         }
         Ok(())
-- 
GitLab


From 9338f1eb0d2f26a76c122378e609a9801cff8947 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 12:18:41 -0600
Subject: [PATCH 10/16] Fixes to optimizations and interface

---
 Cargo.lock                          |   1 +
 hercules_ir/Cargo.toml              |   1 +
 hercules_ir/src/build.rs            |  21 +-
 hercules_ir/src/dc_normalization.rs | 344 +++++++++++++++-------------
 hercules_ir/src/ir.rs               |  28 +++
 hercules_ir/src/parse.rs            |   5 +-
 hercules_ir/src/typecheck.rs        |  17 +-
 hercules_opt/src/editor.rs          |   5 +-
 hercules_opt/src/gcm.rs             |  18 +-
 hercules_opt/src/lift_dc_math.rs    |  14 +-
 hercules_opt/src/utils.rs           |  44 +---
 11 files changed, 267 insertions(+), 231 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 8bb64bd2..c02ec0b2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -824,6 +824,7 @@ name = "hercules_ir"
 version = "0.1.0"
 dependencies = [
  "bitvec",
+ "either",
  "nom",
  "ordered-float",
  "rand",
diff --git a/hercules_ir/Cargo.toml b/hercules_ir/Cargo.toml
index deda9cc5..26950d4b 100644
--- a/hercules_ir/Cargo.toml
+++ b/hercules_ir/Cargo.toml
@@ -10,3 +10,4 @@ nom = "*"
 ordered-float = { version = "*", features = ["serde"] }
 bitvec = "*"
 serde = { version = "*", features = ["derive"] }
+either = "*"
diff --git a/hercules_ir/src/build.rs b/hercules_ir/src/build.rs
index b7844806..b8044045 100644
--- a/hercules_ir/src/build.rs
+++ b/hercules_ir/src/build.rs
@@ -1,4 +1,5 @@
 use std::collections::{HashMap, HashSet};
+use std::ops::Deref;
 
 use crate::*;
 
@@ -26,7 +27,7 @@ pub struct Builder<'a> {
 }
 
 impl<'a> DynamicConstantView for Builder<'a> {
-    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+    fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_ {
         &self.module.dynamic_constants[id.idx()]
     }
 
@@ -412,11 +413,11 @@ impl<'a> Builder<'a> {
     }
 
     pub fn create_dynamic_constant_constant(&mut self, val: usize) -> DynamicConstantID {
-        dc_const(val, self)
+        self.dc_const(val)
     }
 
     pub fn create_dynamic_constant_parameter(&mut self, idx: usize) -> DynamicConstantID {
-        dc_param(idx, self)
+        self.dc_param(idx)
     }
 
     pub fn create_dynamic_constant_add(
@@ -424,14 +425,14 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_add(vec![x, y], self)
+        self.dc_add(vec![x, y])
     }
 
     pub fn create_dynamic_constant_add_many(
         &mut self,
         xs: Vec<DynamicConstantID>,
     ) -> DynamicConstantID {
-        dc_add(xs, self)
+        self.dc_add(xs)
     }
 
     pub fn create_dynamic_constant_sub(
@@ -439,7 +440,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_sub(x, y, self)
+        self.dc_sub(x, y)
     }
 
     pub fn create_dynamic_constant_mul(
@@ -447,14 +448,14 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_mul(vec![x, y], self)
+        self.dc_mul(vec![x, y])
     }
 
     pub fn create_dynamic_constant_mul_many(
         &mut self,
         xs: Vec<DynamicConstantID>,
     ) -> DynamicConstantID {
-        dc_mul(xs, self)
+        self.dc_mul(xs)
     }
 
     pub fn create_dynamic_constant_div(
@@ -462,7 +463,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_div(x, y, self)
+        self.dc_div(x, y)
     }
 
     pub fn create_dynamic_constant_rem(
@@ -470,7 +471,7 @@ impl<'a> Builder<'a> {
         x: DynamicConstantID,
         y: DynamicConstantID,
     ) -> DynamicConstantID {
-        dc_rem(x, y, self)
+        self.dc_rem(x, y)
     }
 
     pub fn create_field_index(&self, idx: usize) -> Index {
diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index 4705eac3..ca76f3f7 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -1,206 +1,228 @@
 use crate::*;
 
 use std::cmp::{min, max};
+use std::ops::Deref;
 
-pub trait DynamicConstantView {
-    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant;
+use either::Either;
+
+pub trait DynamicConstantView
+{
+    fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_;
     fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID;
-}
 
-pub fn dc_const<T: DynamicConstantView>(
-    val: usize,
-    view: &mut T,
-) -> DynamicConstantID {
-    view.add_dynconst(DynamicConstant::Constant(val))
-}
+    fn dc_const(
+        &mut self,
+        val: usize,
+    ) -> DynamicConstantID {
+        self.add_dynconst(DynamicConstant::Constant(val))
+    }
 
-pub fn dc_param<T: DynamicConstantView>(
-    index: usize,
-    view: &mut T,
-) -> DynamicConstantID {
-    view.add_dynconst(DynamicConstant::Parameter(index))
-}
+    fn dc_param(
+        &mut self,
+        index: usize,
+    ) -> DynamicConstantID {
+        self.add_dynconst(DynamicConstant::Parameter(index))
+    }
 
-pub fn dc_add<T: DynamicConstantView>(
-    dcs: Vec<DynamicConstantID>,
-    view: &mut T,
-) -> DynamicConstantID {
-    let mut constant_val = 0;
-    let mut fields = vec![];
+    fn dc_add(
+        &mut self,
+        dcs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        let mut constant_val = 0;
+        let mut fields = vec![];
 
-    for dc in dcs {
-        match view.get_dynconst(dc) {
-            DynamicConstant::Constant(x) => constant_val += x,
-            DynamicConstant::Add(xs) => fields.extend_from_slice(xs),
-            _ => fields.push(dc),
+        for dc in dcs {
+            match self.get_dynconst(dc).deref() {
+                DynamicConstant::Constant(x) => constant_val += x,
+                DynamicConstant::Add(xs) => fields.extend_from_slice(xs),
+                _ => fields.push(dc),
+            }
         }
-    }
 
-    // If either there are no fields or the constant is non-zero, add it
-    if constant_val != 0 || fields.len() == 0 {
-        fields.push(view.add_dynconst(DynamicConstant::Constant(constant_val)));
-    }
+        // If either there are no fields or the constant is non-zero, add it
+        if constant_val != 0 || fields.len() == 0 {
+            fields.push(self.add_dynconst(DynamicConstant::Constant(constant_val)));
+        }
 
-    if fields.len() <= 1 {
-        // If there is only one term to add, just return it
-        fields[0]
-    } else {
-        fields.sort();
-        view.add_dynconst(DynamicConstant::Add(fields))
+        if fields.len() <= 1 {
+            // If there is only one term to add, just return it
+            fields[0]
+        } else {
+            fields.sort();
+            self.add_dynconst(DynamicConstant::Add(fields))
+        }
     }
-}
 
-pub fn dc_mul<T: DynamicConstantView>(
-    dcs: Vec<DynamicConstantID>,
-    view: &mut T,
-) -> DynamicConstantID {
-    let mut constant_val = 1;
-    let mut fields = vec![];
+    fn dc_mul(
+        &mut self,
+        dcs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        let mut constant_val = 1;
+        let mut fields = vec![];
 
-    for dc in dcs {
-        match view.get_dynconst(dc) {
-            DynamicConstant::Constant(x) => constant_val *= x,
-            DynamicConstant::Mul(xs) => fields.extend_from_slice(xs),
-            _ => fields.push(dc),
+        for dc in dcs {
+            match self.get_dynconst(dc).deref() {
+                DynamicConstant::Constant(x) => constant_val *= x,
+                DynamicConstant::Mul(xs) => fields.extend_from_slice(xs),
+                _ => fields.push(dc),
+            }
         }
-    }
 
-    if constant_val == 0 {
-        return view.add_dynconst(DynamicConstant::Constant(0));
-    }
+        if constant_val == 0 {
+            return self.add_dynconst(DynamicConstant::Constant(0));
+        }
 
-    if constant_val != 1 || fields.len() == 0 {
-        fields.push(view.add_dynconst(DynamicConstant::Constant(constant_val)));
-    }
+        if constant_val != 1 || fields.len() == 0 {
+            fields.push(self.add_dynconst(DynamicConstant::Constant(constant_val)));
+        }
 
-    if fields.len() <= 1 {
-        fields[0]
-    } else {
-        fields.sort();
-        view.add_dynconst(DynamicConstant::Mul(fields))
+        if fields.len() <= 1 {
+            fields[0]
+        } else {
+            fields.sort();
+            self.add_dynconst(DynamicConstant::Mul(fields))
+        }
     }
-}
 
-pub fn dc_min<T: DynamicConstantView>(
-    dcs: Vec<DynamicConstantID>,
-    view: &mut T,
-) -> DynamicConstantID {
-    let mut constant_val : Option<usize> = None;
-    let mut fields = vec![];
-
-    for dc in dcs {
-        match view.get_dynconst(dc) {
-            DynamicConstant::Constant(x) => {
-                if let Some(cur_min) = constant_val {
-                    constant_val = Some(min(cur_min, *x));
-                } else {
-                    constant_val = Some(*x);
+    fn dc_min(
+        &mut self,
+        dcs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        let mut constant_val : Option<usize> = None;
+        let mut fields = vec![];
+
+        for dc in dcs {
+            match self.get_dynconst(dc).deref() {
+                DynamicConstant::Constant(x) => {
+                    if let Some(cur_min) = constant_val {
+                        constant_val = Some(min(cur_min, *x));
+                    } else {
+                        constant_val = Some(*x);
+                    }
                 }
+                DynamicConstant::Min(xs) => fields.extend_from_slice(xs),
+                _ => fields.push(dc),
             }
-            DynamicConstant::Min(xs) => fields.extend_from_slice(xs),
-            _ => fields.push(dc),
         }
-    }
 
-    if let Some(const_val) = constant_val {
-        fields.push(view.add_dynconst(DynamicConstant::Constant(const_val)));
-    }
+        if let Some(const_val) = constant_val {
+            fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
+        }
 
-    assert!(fields.len() > 0, "Min of 0 dynamic constant expressions is undefined");
+        assert!(fields.len() > 0, "Min of 0 dynamic constant expressions is undefined");
 
-    if fields.len() <= 1 {
-        fields[0]
-    } else {
-        fields.sort();
-        view.add_dynconst(DynamicConstant::Min(fields))
+        if fields.len() <= 1 {
+            fields[0]
+        } else {
+            fields.sort();
+            self.add_dynconst(DynamicConstant::Min(fields))
+        }
     }
-}
 
-pub fn dc_max<T: DynamicConstantView>(
-    dcs: Vec<DynamicConstantID>,
-    view: &mut T,
-) -> DynamicConstantID {
-    let mut constant_val : Option<usize> = None;
-    let mut fields = vec![];
-
-    for dc in dcs {
-        match view.get_dynconst(dc) {
-            DynamicConstant::Constant(x) => {
-                if let Some(cur_max) = constant_val {
-                    constant_val = Some(max(cur_max, *x));
-                } else {
-                    constant_val = Some(*x);
+    fn dc_max(
+        &mut self,
+        dcs: Vec<DynamicConstantID>,
+    ) -> DynamicConstantID {
+        let mut constant_val : Option<usize> = None;
+        let mut fields = vec![];
+
+        for dc in dcs {
+            match self.get_dynconst(dc).deref() {
+                DynamicConstant::Constant(x) => {
+                    if let Some(cur_max) = constant_val {
+                        constant_val = Some(max(cur_max, *x));
+                    } else {
+                        constant_val = Some(*x);
+                    }
                 }
+                DynamicConstant::Max(xs) => fields.extend_from_slice(xs),
+                _ => fields.push(dc),
             }
-            DynamicConstant::Max(xs) => fields.extend_from_slice(xs),
-            _ => fields.push(dc),
         }
-    }
 
-    if let Some(const_val) = constant_val {
-        fields.push(view.add_dynconst(DynamicConstant::Constant(const_val)));
-    }
+        if let Some(const_val) = constant_val {
+            fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
+        }
 
-    assert!(fields.len() > 0, "Max of 0 dynamic constant expressions is undefined");
+        assert!(fields.len() > 0, "Max of 0 dynamic constant expressions is undefined");
 
-    if fields.len() <= 1 {
-        fields[0]
-    } else {
-        fields.sort();
-        view.add_dynconst(DynamicConstant::Max(fields))
+        if fields.len() <= 1 {
+            fields[0]
+        } else {
+            fields.sort();
+            self.add_dynconst(DynamicConstant::Max(fields))
+        }
     }
-}
 
-pub fn dc_sub<T: DynamicConstantView>(
-    x: DynamicConstantID,
-    y: DynamicConstantID,
-    view: &mut T,
-) -> DynamicConstantID {
-    match (view.get_dynconst(x), view.get_dynconst(y)) {
-        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            view.add_dynconst(DynamicConstant::Constant(x - y)),
-        (_, DynamicConstant::Constant(0)) => x,
-        _ => view.add_dynconst(DynamicConstant::Sub(x, y)),
+    fn dc_sub(
+        &mut self,
+        x: DynamicConstantID,
+        y: DynamicConstantID,
+    ) -> DynamicConstantID {
+        let dc =
+            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+                    Either::Left(DynamicConstant::Constant(x - y)),
+                (_, DynamicConstant::Constant(0)) => Either::Right(x),
+                _ => Either::Left(DynamicConstant::Sub(x, y)),
+            };
+
+        match dc {
+            Either::Left(dc) => self.add_dynconst(dc),
+            Either::Right(id) => id,
+        }
     }
-}
 
-pub fn dc_div<T: DynamicConstantView>(
-    x: DynamicConstantID,
-    y: DynamicConstantID,
-    view: &mut T,
-) -> DynamicConstantID {
-    match (view.get_dynconst(x), view.get_dynconst(y)) {
-        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            view.add_dynconst(DynamicConstant::Constant(x / y)),
-        (_, DynamicConstant::Constant(1)) => x,
-        _ => view.add_dynconst(DynamicConstant::Div(x, y)),
+    fn dc_div(
+        &mut self,
+        x: DynamicConstantID,
+        y: DynamicConstantID,
+    ) -> DynamicConstantID {
+        let dc =
+            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+                    Either::Left(DynamicConstant::Constant(x / y)),
+                (_, DynamicConstant::Constant(1)) => Either::Right(x),
+                _ => Either::Left(DynamicConstant::Div(x, y)),
+            };
+
+        match dc {
+            Either::Left(dc) => self.add_dynconst(dc),
+            Either::Right(id) => id,
+        }
     }
-}
 
-pub fn dc_rem<T: DynamicConstantView>(
-    x: DynamicConstantID,
-    y: DynamicConstantID,
-    view: &mut T,
-) -> DynamicConstantID {
-    match (view.get_dynconst(x), view.get_dynconst(y)) {
-        (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-            view.add_dynconst(DynamicConstant::Constant(x % y)),
-        _ => view.add_dynconst(DynamicConstant::Rem(x, y)),
+    fn dc_rem(
+        &mut self,
+        x: DynamicConstantID,
+        y: DynamicConstantID,
+    ) -> DynamicConstantID {
+        let dc =
+            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
+                    Either::Left(DynamicConstant::Constant(x % y)),
+                _ => Either::Left(DynamicConstant::Rem(x, y)),
+            };
+
+        match dc {
+            Either::Left(dc) => self.add_dynconst(dc),
+            Either::Right(id) => id,
+        }
     }
-}
 
-pub fn dc_normalize<T: DynamicConstantView>(
-    dc: DynamicConstant,
-    view: &mut T,
-) -> DynamicConstantID {
-    match dc {
-        DynamicConstant::Add(xs) => dc_add(xs, view),
-        DynamicConstant::Mul(xs) => dc_mul(xs, view),
-        DynamicConstant::Min(xs) => dc_min(xs, view),
-        DynamicConstant::Max(xs) => dc_max(xs, view),
-        DynamicConstant::Sub(x, y) => dc_sub(x, y, view),
-        DynamicConstant::Div(x, y) => dc_div(x, y, view),
-        DynamicConstant::Rem(x, y) => dc_rem(x, y, view),
-        _ => view.add_dynconst(dc),
+    fn dc_normalize(
+        &mut self,
+        dc: DynamicConstant,
+    ) -> DynamicConstantID {
+        match dc {
+            DynamicConstant::Add(xs) => self.dc_add(xs),
+            DynamicConstant::Mul(xs) => self.dc_mul(xs),
+            DynamicConstant::Min(xs) => self.dc_min(xs),
+            DynamicConstant::Max(xs) => self.dc_max(xs),
+            DynamicConstant::Sub(x, y) => self.dc_sub(x, y),
+            DynamicConstant::Div(x, y) => self.dc_div(x, y),
+            DynamicConstant::Rem(x, y) => self.dc_rem(x, y),
+            _ => self.add_dynconst(dc),
+        }
     }
 }
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index 784bc2de..ba8d607e 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -1020,6 +1020,34 @@ impl Constant {
 }
 
 impl DynamicConstant {
+    pub fn add(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Add(vec![x, y])
+    }
+    
+    pub fn sub(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Sub(x, y)
+    }
+    
+    pub fn mul(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Mul(vec![x, y])
+    }
+    
+    pub fn div(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Div(x, y)
+    }
+    
+    pub fn rem(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Rem(x, y)
+    }
+    
+    pub fn min(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Min(vec![x, y])
+    }
+    
+    pub fn max(x: DynamicConstantID, y: DynamicConstantID) -> Self {
+        Self::Max(vec![x, y])
+    }
+
     pub fn is_parameter(&self) -> bool {
         if let DynamicConstant::Parameter(_) = self {
             true
diff --git a/hercules_ir/src/parse.rs b/hercules_ir/src/parse.rs
index 1f6d931e..257dd4d9 100644
--- a/hercules_ir/src/parse.rs
+++ b/hercules_ir/src/parse.rs
@@ -1,5 +1,6 @@
 use std::cell::RefCell;
 use std::collections::{HashMap, HashSet};
+use std::ops::Deref;
 use std::str::FromStr;
 
 use crate::*;
@@ -87,7 +88,7 @@ impl<'a> Context<'a> {
     }
 
     fn get_dynamic_constant_id(&mut self, dynamic_constant: DynamicConstant) -> DynamicConstantID {
-        dc_normalize(dynamic_constant, self)
+        self.dc_normalize(dynamic_constant)
     }
 
     fn get_label_id(&mut self, label: String) -> LabelID {
@@ -102,7 +103,7 @@ impl<'a> Context<'a> {
 }
 
 impl<'a> DynamicConstantView for Context<'a> {
-    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+    fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_ {
         &self.reverse_dynamic_constant_map[&id]
     }
 
diff --git a/hercules_ir/src/typecheck.rs b/hercules_ir/src/typecheck.rs
index 698223b4..fdbbd46e 100644
--- a/hercules_ir/src/typecheck.rs
+++ b/hercules_ir/src/typecheck.rs
@@ -1,5 +1,6 @@
 use std::collections::HashMap;
 use std::iter::zip;
+use std::ops::Deref;
 
 use crate::*;
 
@@ -1081,7 +1082,7 @@ struct DCSubst<'a> {
 }
 
 impl<'a> DynamicConstantView for DCSubst<'a> {
-    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+    fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_ {
         &self.dynamic_constants[id.idx()]
     }
 
@@ -1171,40 +1172,40 @@ impl<'a> DCSubst<'a> {
             DynamicConstant::Parameter(i) => self.dc_args[*i],
             DynamicConstant::Add(xs) => {
                 let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
-                dc_add(sxs, self)
+                self.dc_add(sxs)
             }
             DynamicConstant::Sub(l, r) => {
                 let x = *l;
                 let y = *r;
                 let sx = self.dyn_const_subst(x);
                 let sy = self.dyn_const_subst(y);
-                dc_sub(sx, sy, self)
+                self.dc_sub(sx, sy)
             }
             DynamicConstant::Mul(xs) => {
                 let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
-                dc_mul(sxs, self)
+                self.dc_mul(sxs)
             }
             DynamicConstant::Div(l, r) => {
                 let x = *l;
                 let y = *r;
                 let sx = self.dyn_const_subst(x);
                 let sy = self.dyn_const_subst(y);
-                dc_div(sx, sy, self)
+                self.dc_div(sx, sy)
             }
             DynamicConstant::Rem(l, r) => {
                 let x = *l;
                 let y = *r;
                 let sx = self.dyn_const_subst(x);
                 let sy = self.dyn_const_subst(y);
-                dc_rem(sx, sy, self)
+                self.dc_rem(sx, sy)
             }
             DynamicConstant::Min(xs) => {
                 let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
-                dc_min(sxs, self)
+                self.dc_min(sxs)
             }
             DynamicConstant::Max(xs) => {
                 let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
-                dc_max(sxs, self)
+                self.dc_max(sxs)
             }
         }
     }
diff --git a/hercules_opt/src/editor.rs b/hercules_opt/src/editor.rs
index 8164ff9d..cbfa49c0 100644
--- a/hercules_opt/src/editor.rs
+++ b/hercules_opt/src/editor.rs
@@ -8,6 +8,7 @@ use either::Either;
 
 use hercules_ir::def_use::*;
 use hercules_ir::ir::*;
+use hercules_ir::DynamicConstantView;
 
 /*
  * Helper object for editing Hercules functions in a trackable manner. Edits
@@ -725,7 +726,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
     }
 
     pub fn add_dynamic_constant(&mut self, dynamic_constant: DynamicConstant) -> DynamicConstantID {
-        dc_normalize(dynamic_constant, self)
+        self.dc_normalize(dynamic_constant)
     }
 
     pub fn get_dynamic_constant(
@@ -756,7 +757,7 @@ impl<'a, 'b> FunctionEdit<'a, 'b> {
 }
 
 impl<'a, 'b> DynamicConstantView for FunctionEdit<'a, 'b> {
-    fn get_dynconst(&self, id: DynamicConstantID) -> &DynamicConstant {
+    fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_ {
         self.get_dynamic_constant(id)
     }
 
diff --git a/hercules_opt/src/gcm.rs b/hercules_opt/src/gcm.rs
index b5822a51..f80973ee 100644
--- a/hercules_opt/src/gcm.rs
+++ b/hercules_opt/src/gcm.rs
@@ -1061,9 +1061,9 @@ fn align(edit: &mut FunctionEdit, mut acc: DynamicConstantID, align: usize) -> D
     if align != 1 {
         let align_dc = edit.add_dynamic_constant(DynamicConstant::Constant(align));
         let align_m1_dc = edit.add_dynamic_constant(DynamicConstant::Constant(align - 1));
-        acc = edit.add_dynamic_constant(DynamicConstant::Add(acc, align_m1_dc));
-        acc = edit.add_dynamic_constant(DynamicConstant::Div(acc, align_dc));
-        acc = edit.add_dynamic_constant(DynamicConstant::Mul(acc, align_dc));
+        acc = edit.add_dynamic_constant(DynamicConstant::add(acc, align_m1_dc));
+        acc = edit.add_dynamic_constant(DynamicConstant::div(acc, align_dc));
+        acc = edit.add_dynamic_constant(DynamicConstant::mul(acc, align_dc));
     }
     acc
 }
@@ -1095,7 +1095,7 @@ fn type_size(edit: &mut FunctionEdit, ty_id: TypeID, alignments: &Vec<usize>) ->
                 // the field.
                 let field_size = type_size(edit, field, alignments);
                 acc_size = align(edit, acc_size, alignments[field.idx()]);
-                acc_size = edit.add_dynamic_constant(DynamicConstant::Add(acc_size, field_size));
+                acc_size = edit.add_dynamic_constant(DynamicConstant::add(acc_size, field_size));
             }
             // Finally, round up to the alignment of the whole product, since
             // the size needs to be a multiple of the alignment.
@@ -1109,11 +1109,11 @@ fn type_size(edit: &mut FunctionEdit, ty_id: TypeID, alignments: &Vec<usize>) ->
                 // Pick the size of the largest variant, since that's the most
                 // memory we would need.
                 let variant_size = type_size(edit, variant, alignments);
-                acc_size = edit.add_dynamic_constant(DynamicConstant::Max(acc_size, variant_size));
+                acc_size = edit.add_dynamic_constant(DynamicConstant::max(acc_size, variant_size));
             }
             // Add one byte for the discriminant and align the whole summation.
             let one = edit.add_dynamic_constant(DynamicConstant::Constant(1));
-            acc_size = edit.add_dynamic_constant(DynamicConstant::Add(acc_size, one));
+            acc_size = edit.add_dynamic_constant(DynamicConstant::add(acc_size, one));
             acc_size = align(edit, acc_size, alignments[ty_id.idx()]);
             acc_size
         }
@@ -1121,7 +1121,7 @@ fn type_size(edit: &mut FunctionEdit, ty_id: TypeID, alignments: &Vec<usize>) ->
             // The layout of an array is row-major linear in memory.
             let mut acc_size = type_size(edit, elem, alignments);
             for bound in bounds {
-                acc_size = edit.add_dynamic_constant(DynamicConstant::Mul(acc_size, bound));
+                acc_size = edit.add_dynamic_constant(DynamicConstant::mul(acc_size, bound));
             }
             acc_size
         }
@@ -1157,7 +1157,7 @@ fn object_allocation(
                         *total = align(&mut edit, *total, alignments[typing[id.idx()].idx()]);
                         offsets.insert(id, *total);
                         let type_size = type_size(&mut edit, typing[id.idx()], alignments);
-                        *total = edit.add_dynamic_constant(DynamicConstant::Add(*total, type_size));
+                        *total = edit.add_dynamic_constant(DynamicConstant::add(*total, type_size));
                     }
                 }
                 Node::Call {
@@ -1199,7 +1199,7 @@ fn object_allocation(
                                     &mut edit,
                                 );
                             }
-                            *total = edit.add_dynamic_constant(DynamicConstant::Add(
+                            *total = edit.add_dynamic_constant(DynamicConstant::add(
                                 *total,
                                 callee_backing_size,
                             ));
diff --git a/hercules_opt/src/lift_dc_math.rs b/hercules_opt/src/lift_dc_math.rs
index afdb2120..8256c889 100644
--- a/hercules_opt/src/lift_dc_math.rs
+++ b/hercules_opt/src/lift_dc_math.rs
@@ -41,11 +41,11 @@ pub fn lift_dc_math(editor: &mut FunctionEditor) {
                     continue;
                 };
                 match op {
-                    BinaryOperator::Add => DynamicConstant::Add(left, right),
-                    BinaryOperator::Sub => DynamicConstant::Sub(left, right),
-                    BinaryOperator::Mul => DynamicConstant::Mul(left, right),
-                    BinaryOperator::Div => DynamicConstant::Div(left, right),
-                    BinaryOperator::Rem => DynamicConstant::Rem(left, right),
+                    BinaryOperator::Add => DynamicConstant::add(left, right),
+                    BinaryOperator::Sub => DynamicConstant::sub(left, right),
+                    BinaryOperator::Mul => DynamicConstant::mul(left, right),
+                    BinaryOperator::Div => DynamicConstant::div(left, right),
+                    BinaryOperator::Rem => DynamicConstant::rem(left, right),
                     _ => {
                         continue;
                     }
@@ -64,8 +64,8 @@ pub fn lift_dc_math(editor: &mut FunctionEditor) {
                     continue;
                 };
                 match intrinsic {
-                    Intrinsic::Min => DynamicConstant::Min(left, right),
-                    Intrinsic::Max => DynamicConstant::Max(left, right),
+                    Intrinsic::Min => DynamicConstant::min(left, right),
+                    Intrinsic::Max => DynamicConstant::max(left, right),
                     _ => {
                         continue;
                     }
diff --git a/hercules_opt/src/utils.rs b/hercules_opt/src/utils.rs
index aa0d53fe..191c0502 100644
--- a/hercules_opt/src/utils.rs
+++ b/hercules_opt/src/utils.rs
@@ -87,14 +87,9 @@ pub(crate) fn substitute_dynamic_constants(
     match dc_clone {
         DynamicConstant::Constant(_) | DynamicConstant::Parameter(_) => dc_c,
         // This is a certified Rust moment.
-        DynamicConstant::Add(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
-            if new_left != left || new_right != right {
-                edit.add_dynamic_constant(DynamicConstant::Add(new_left, new_right))
-            } else {
-                dc_c
-            }
+        DynamicConstant::Add(xs) => {
+            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
+            edit.add_dynamic_constant(DynamicConstant::Add(new_xs))
         }
         DynamicConstant::Sub(left, right) => {
             let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
@@ -105,14 +100,9 @@ pub(crate) fn substitute_dynamic_constants(
                 dc_c
             }
         }
-        DynamicConstant::Mul(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
-            if new_left != left || new_right != right {
-                edit.add_dynamic_constant(DynamicConstant::Mul(new_left, new_right))
-            } else {
-                dc_c
-            }
+        DynamicConstant::Mul(xs) => {
+            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
+            edit.add_dynamic_constant(DynamicConstant::Mul(new_xs))
         }
         DynamicConstant::Div(left, right) => {
             let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
@@ -132,23 +122,13 @@ pub(crate) fn substitute_dynamic_constants(
                 dc_c
             }
         }
-        DynamicConstant::Min(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
-            if new_left != left || new_right != right {
-                edit.add_dynamic_constant(DynamicConstant::Min(new_left, new_right))
-            } else {
-                dc_c
-            }
+        DynamicConstant::Min(xs) => {
+            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
+            edit.add_dynamic_constant(DynamicConstant::Min(new_xs))
         }
-        DynamicConstant::Max(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
-            if new_left != left || new_right != right {
-                edit.add_dynamic_constant(DynamicConstant::Max(new_left, new_right))
-            } else {
-                dc_c
-            }
+        DynamicConstant::Max(xs) => {
+            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
+            edit.add_dynamic_constant(DynamicConstant::Max(new_xs))
         }
     }
 }
-- 
GitLab


From c7b53e45c7a6a8202154bfc73453cc9a497f2e72 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 14:03:24 -0600
Subject: [PATCH 11/16] Fix dynamic constant substitution

---
 hercules_opt/src/gcm.rs                  |  29 ++---
 hercules_opt/src/inline.rs               |  37 +------
 hercules_opt/src/interprocedural_sroa.rs |  55 +++-------
 hercules_opt/src/utils.rs                | 130 ++++++++++++-----------
 juno_samples/concat/src/main.rs          |  16 +++
 5 files changed, 112 insertions(+), 155 deletions(-)

diff --git a/hercules_opt/src/gcm.rs b/hercules_opt/src/gcm.rs
index f80973ee..a22805b0 100644
--- a/hercules_opt/src/gcm.rs
+++ b/hercules_opt/src/gcm.rs
@@ -1166,7 +1166,10 @@ fn object_allocation(
                     ref dynamic_constants,
                     args: _,
                 } => {
-                    let dynamic_constants = dynamic_constants.clone();
+                    let dynamic_constants = dynamic_constants.to_vec();
+                    let dc_args = (0..dynamic_constants.len()).map(|i| edit.add_dynamic_constant(DynamicConstant::Parameter(i)));
+                    let substs = dc_args.zip(dynamic_constants.into_iter()).collect::<HashMap<_, _>>();
+
                     for device in BACKED_DEVICES {
                         if let Some(mut callee_backing_size) = backing_allocations[&callee]
                             .get(&device)
@@ -1180,25 +1183,11 @@ fn object_allocation(
                             offsets.insert(id, *total);
                             // Substitute the dynamic constant parameters in the
                             // callee's backing size.
-                            let first_dc = edit.num_dynamic_constants() + 10000;
-                            for (p_idx, dc_n) in zip(0..dynamic_constants.len(), first_dc..) {
-                                let dc_a =
-                                    edit.add_dynamic_constant(DynamicConstant::Parameter(p_idx));
-                                callee_backing_size = substitute_dynamic_constants(
-                                    dc_a,
-                                    DynamicConstantID::new(dc_n),
-                                    callee_backing_size,
-                                    &mut edit,
-                                );
-                            }
-                            for (dc_n, dc_b) in zip(first_dc.., dynamic_constants.iter()) {
-                                callee_backing_size = substitute_dynamic_constants(
-                                    DynamicConstantID::new(dc_n),
-                                    *dc_b,
-                                    callee_backing_size,
-                                    &mut edit,
-                                );
-                            }
+                            callee_backing_size = substitute_dynamic_constants(
+                                &substs,
+                                callee_backing_size,
+                                &mut edit,
+                            );
                             *total = edit.add_dynamic_constant(DynamicConstant::add(
                                 *total,
                                 callee_backing_size,
diff --git a/hercules_opt/src/inline.rs b/hercules_opt/src/inline.rs
index 1d2bac97..f63fa44c 100644
--- a/hercules_opt/src/inline.rs
+++ b/hercules_opt/src/inline.rs
@@ -119,7 +119,8 @@ fn inline_func(
 
         // Assemble all the info we'll need to do the edit.
         let dcs_a = &dc_param_idx_to_dc_id[..dynamic_constants.len()];
-        let dcs_b = dynamic_constants.clone();
+        let dcs_b = dynamic_constants.to_vec();
+        let substs = dcs_a.iter().map(|i| *i).zip(dcs_b.into_iter()).collect::<HashMap<_, _>>();
         let args = args.clone();
         let old_num_nodes = editor.func().nodes.len();
         let old_id_to_new_id = |old_id: NodeID| NodeID::new(old_id.idx() + old_num_nodes);
@@ -163,39 +164,7 @@ fn inline_func(
                     || node.is_dynamic_constant()
                     || node.is_call()
                 {
-                    // We have to perform the subsitution in two steps. First,
-                    // we map every dynamic constant A to a non-sense dynamic
-                    // constant ID. Second, we map each non-sense dynamic
-                    // constant ID to the appropriate dynamic constant B. Why
-                    // not just do this in one step from A to B? We update
-                    // dynamic constants one at a time, so imagine the following
-                    // A -> B mappings:
-                    // ID 0 -> ID 1
-                    // ID 1 -> ID 0
-                    // First, we apply the first mapping. This changes all
-                    // references to dynamic constant 0 to dynamic constant 1.
-                    // Then, we apply the second mapping. This updates all
-                    // already present references to dynamic constant 1, as well
-                    // as the new references we just made in the first step. We
-                    // actually want to institute all the updates
-                    // *simultaneously*, hence the two step maneuver.
-                    let first_dc = edit.num_dynamic_constants() + 10000;
-                    for (dc_a, dc_n) in zip(dcs_a, first_dc..) {
-                        substitute_dynamic_constants_in_node(
-                            *dc_a,
-                            DynamicConstantID::new(dc_n),
-                            &mut node,
-                            &mut edit,
-                        );
-                    }
-                    for (dc_n, dc_b) in zip(first_dc.., dcs_b.iter()) {
-                        substitute_dynamic_constants_in_node(
-                            DynamicConstantID::new(dc_n),
-                            *dc_b,
-                            &mut node,
-                            &mut edit,
-                        );
-                    }
+                    substitute_dynamic_constants_in_node(&substs, &mut node, &mut edit);
                 }
                 let mut uses = get_uses_mut(&mut node);
                 for u in uses.as_mut() {
diff --git a/hercules_opt/src/interprocedural_sroa.rs b/hercules_opt/src/interprocedural_sroa.rs
index f597cd80..1797427e 100644
--- a/hercules_opt/src/interprocedural_sroa.rs
+++ b/hercules_opt/src/interprocedural_sroa.rs
@@ -313,31 +313,20 @@ fn compress_return_products(editors: &mut Vec<FunctionEditor>, all_callsites_edi
             // If this becomes a common pattern, it would be worth creating
             // a better abstraction around bulk replacement.
 
-            let new_dcs = (*dynamic_constants).clone();
+            let new_dcs = (*dynamic_constants).to_vec();
+            let old_dcs = dc_param_idx_to_dc_id[..new_dcs.len()].to_vec();
+            assert_eq!(old_dcs.len(), new_dcs.len());
+            let substs = old_dcs.into_iter().zip(new_dcs.into_iter()).collect::<HashMap<_, _>>();
 
             let edit_successful = editor.edit(|mut edit| {
-                let old_dcs = dc_param_idx_to_dc_id[..new_dcs.len()].to_vec().clone();
                 let mut substituted = old_return_type_ids[function_id.idx()];
 
-                assert_eq!(old_dcs.len(), new_dcs.len());
-                let first_dc = edit.num_dynamic_constants() + 10000;
-                for (dc_a, dc_n) in zip(old_dcs, first_dc..) {
-                    substituted = substitute_dynamic_constants_in_type(
-                        dc_a,
-                        DynamicConstantID::new(dc_n),
-                        substituted,
+                let substituted =
+                    substitute_dynamic_constants_in_type(
+                        &substs,
+                        old_return_type_ids[function_id.idx()],
                         &mut edit,
                     );
-                }
-
-                for (dc_n, dc_b) in zip(first_dc.., new_dcs.iter()) {
-                    substituted = substitute_dynamic_constants_in_type(
-                        DynamicConstantID::new(dc_n),
-                        *dc_b,
-                        substituted,
-                        &mut edit,
-                    );
-                }
 
                 let (expanded_product, readers) =
                     uncompress_product(&mut edit, &call_node_id, &substituted);
@@ -419,34 +408,24 @@ fn remove_return_singletons(editors: &mut Vec<FunctionEditor>, all_callsites_edi
         for call_node_id in call_node_ids {
             let (_, function, dc_args, _) =
                 editor.func().nodes[call_node_id.idx()].try_call().unwrap();
-            let dc_args = dc_args.clone();
+
+            let dc_args = dc_args.to_vec();
 
             if singleton_removed[function.idx()] {
                 let edit_successful = editor.edit(|mut edit| {
-                    let mut substituted = old_return_type_ids[function.idx()];
-                    let first_dc = edit.num_dynamic_constants() + 10000;
-                    let dc_params: Vec<_> = (0..dc_args.len())
+                    let dc_params = (0..dc_args.len())
                         .map(|param_idx| {
                             edit.add_dynamic_constant(DynamicConstant::Parameter(param_idx))
                         })
-                        .collect();
-                    for (dc_a, dc_n) in zip(dc_params, first_dc..) {
-                        substituted = substitute_dynamic_constants_in_type(
-                            dc_a,
-                            DynamicConstantID::new(dc_n),
-                            substituted,
-                            &mut edit,
-                        );
-                    }
+                        .collect::<Vec<_>>();
+                    let substs = dc_params.into_iter().zip(dc_args.into_iter()).collect::<HashMap<_, _>>();
 
-                    for (dc_n, dc_b) in zip(first_dc.., dc_args.iter()) {
-                        substituted = substitute_dynamic_constants_in_type(
-                            DynamicConstantID::new(dc_n),
-                            *dc_b,
-                            substituted,
+                    let substituted =
+                        substitute_dynamic_constants_in_type(
+                            &substs,
+                            old_return_type_ids[function.idx()],
                             &mut edit,
                         );
-                    }
                     let empty_constant_id = edit.add_zero_constant(substituted);
                     let empty_node_id = edit.add_node(Node::Constant {
                         id: empty_constant_id,
diff --git a/hercules_opt/src/utils.rs b/hercules_opt/src/utils.rs
index 191c0502..08dbd131 100644
--- a/hercules_opt/src/utils.rs
+++ b/hercules_opt/src/utils.rs
@@ -1,3 +1,4 @@
+use std::collections::HashMap;
 use std::iter::zip;
 
 use hercules_ir::def_use::*;
@@ -6,12 +7,11 @@ use hercules_ir::ir::*;
 use crate::*;
 
 /*
- * Substitute all uses of a dynamic constant A with dynamic constant B in a
- * type. Return the substituted version of the type, once memozied.
+ * Substitute all uses of dynamic constants in a type that are keys in the substs map with the
+ * dynamic constant value for that key. Return the substituted version of the type, once memoized.
  */
 pub(crate) fn substitute_dynamic_constants_in_type(
-    dc_a: DynamicConstantID,
-    dc_b: DynamicConstantID,
+    substs: &HashMap<DynamicConstantID, DynamicConstantID>,
     ty: TypeID,
     edit: &mut FunctionEdit,
 ) -> TypeID {
@@ -21,7 +21,7 @@ pub(crate) fn substitute_dynamic_constants_in_type(
         Type::Product(ref fields) => {
             let new_fields = fields
                 .into_iter()
-                .map(|field_id| substitute_dynamic_constants_in_type(dc_a, dc_b, *field_id, edit))
+                .map(|field_id| substitute_dynamic_constants_in_type(substs, *field_id, edit))
                 .collect();
             if new_fields != *fields {
                 edit.add_type(Type::Product(new_fields))
@@ -33,7 +33,7 @@ pub(crate) fn substitute_dynamic_constants_in_type(
             let new_variants = variants
                 .into_iter()
                 .map(|variant_id| {
-                    substitute_dynamic_constants_in_type(dc_a, dc_b, *variant_id, edit)
+                    substitute_dynamic_constants_in_type(substs, *variant_id, edit)
                 })
                 .collect();
             if new_variants != *variants {
@@ -43,10 +43,10 @@ pub(crate) fn substitute_dynamic_constants_in_type(
             }
         }
         Type::Array(elem_ty, ref dims) => {
-            let new_elem_ty = substitute_dynamic_constants_in_type(dc_a, dc_b, elem_ty, edit);
+            let new_elem_ty = substitute_dynamic_constants_in_type(substs, elem_ty, edit);
             let new_dims = dims
                 .into_iter()
-                .map(|dim_id| substitute_dynamic_constants(dc_a, dc_b, *dim_id, edit))
+                .map(|dim_id| substitute_dynamic_constants(substs, *dim_id, edit))
                 .collect();
             if new_elem_ty != elem_ty || new_dims != *dims {
                 edit.add_type(Type::Array(new_elem_ty, new_dims))
@@ -59,87 +59,93 @@ pub(crate) fn substitute_dynamic_constants_in_type(
 }
 
 /*
- * Substitute all uses of a dynamic constant A with dynamic constant B in a
- * dynamic constant C. Return the substituted version of C, once memoized. Takes
- * a mutable edit instead of an editor since this may create new dynamic
- * constants, which can only be done inside an edit.
+ * Substitute all uses of dynamic constants in a dynamic constant dc that are keys in the
+ * substs map and replace them with their appropriate replacement values. Return the substituted
+ * version of dc, once memoized. Takes a mutable edit instead of an editor since this may create
+ * new dynamic constants, which can only be done inside an edit.
  */
 pub(crate) fn substitute_dynamic_constants(
-    dc_a: DynamicConstantID,
-    dc_b: DynamicConstantID,
-    dc_c: DynamicConstantID,
+    substs: &HashMap<DynamicConstantID, DynamicConstantID>,
+    dc: DynamicConstantID,
     edit: &mut FunctionEdit,
 ) -> DynamicConstantID {
-    // If C is just A, then just replace all of C with B.
-    if dc_a == dc_c {
-        return dc_b;
+    // If this dynamic constant should be substituted, just return the substitution
+    if let Some(subst) = substs.get(&dc) {
+        return *subst;
     }
 
-    // Since we substitute non-sense dynamic constant IDs earlier, we explicitly
-    // check that the provided ID to replace inside of is valid. Otherwise,
-    // ignore.
-    if dc_c.idx() >= edit.num_dynamic_constants() {
-        return dc_c;
-    }
-
-    // If C is not just A, look inside of it to possibly substitute a child DC.
-    let dc_clone = edit.get_dynamic_constant(dc_c).clone();
+    // Look inside the dynamic constant to perform substitution in its children
+    let dc_clone = edit.get_dynamic_constant(dc).clone();
     match dc_clone {
-        DynamicConstant::Constant(_) | DynamicConstant::Parameter(_) => dc_c,
-        // This is a certified Rust moment.
+        DynamicConstant::Constant(_) | DynamicConstant::Parameter(_) => dc,
         DynamicConstant::Add(xs) => {
-            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
-            edit.add_dynamic_constant(DynamicConstant::Add(new_xs))
+            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            if new_xs != xs {
+                edit.add_dynamic_constant(DynamicConstant::Add(new_xs))
+            } else {
+                dc
+            }
         }
         DynamicConstant::Sub(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
+            let new_left = substitute_dynamic_constants(substs, left, edit);
+            let new_right = substitute_dynamic_constants(substs, right, edit);
             if new_left != left || new_right != right {
                 edit.add_dynamic_constant(DynamicConstant::Sub(new_left, new_right))
             } else {
-                dc_c
+                dc
             }
         }
         DynamicConstant::Mul(xs) => {
-            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
-            edit.add_dynamic_constant(DynamicConstant::Mul(new_xs))
+            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            if new_xs != xs {
+                edit.add_dynamic_constant(DynamicConstant::Mul(new_xs))
+            } else {
+                dc
+            }
         }
         DynamicConstant::Div(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
+            let new_left = substitute_dynamic_constants(substs, left, edit);
+            let new_right = substitute_dynamic_constants(substs, right, edit);
             if new_left != left || new_right != right {
                 edit.add_dynamic_constant(DynamicConstant::Div(new_left, new_right))
             } else {
-                dc_c
+                dc
             }
         }
         DynamicConstant::Rem(left, right) => {
-            let new_left = substitute_dynamic_constants(dc_a, dc_b, left, edit);
-            let new_right = substitute_dynamic_constants(dc_a, dc_b, right, edit);
+            let new_left = substitute_dynamic_constants(substs, left, edit);
+            let new_right = substitute_dynamic_constants(substs, right, edit);
             if new_left != left || new_right != right {
                 edit.add_dynamic_constant(DynamicConstant::Rem(new_left, new_right))
             } else {
-                dc_c
+                dc
             }
         }
         DynamicConstant::Min(xs) => {
-            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
-            edit.add_dynamic_constant(DynamicConstant::Min(new_xs))
+            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            if new_xs != xs {
+                edit.add_dynamic_constant(DynamicConstant::Min(new_xs))
+            } else {
+                dc
+            }
         }
         DynamicConstant::Max(xs) => {
-            let new_xs = xs.into_iter().map(|x| substitute_dynamic_constants(dc_a, dc_b, x, edit)).collect::<Vec<_>>();
-            edit.add_dynamic_constant(DynamicConstant::Max(new_xs))
+            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            if new_xs != xs {
+                edit.add_dynamic_constant(DynamicConstant::Max(new_xs))
+            } else {
+                dc
+            }
         }
     }
 }
 
 /*
- * Substitute all uses of a dynamic constant A with dynamic constant B in a
- * constant. Return the substituted version of the constant, once memozied.
+ * Substitute all uses of the dynamic constants specified by the subst map in a constant. Return
+ * the substituted version of the constant, once memozied.
  */
 pub(crate) fn substitute_dynamic_constants_in_constant(
-    dc_a: DynamicConstantID,
-    dc_b: DynamicConstantID,
+    substs: &HashMap<DynamicConstantID, DynamicConstantID>,
     cons: ConstantID,
     edit: &mut FunctionEdit,
 ) -> ConstantID {
@@ -147,11 +153,11 @@ pub(crate) fn substitute_dynamic_constants_in_constant(
     let cons_clone = edit.get_constant(cons).clone();
     match cons_clone {
         Constant::Product(ty, fields) => {
-            let new_ty = substitute_dynamic_constants_in_type(dc_a, dc_b, ty, edit);
+            let new_ty = substitute_dynamic_constants_in_type(substs, ty, edit);
             let new_fields = fields
                 .iter()
                 .map(|field_id| {
-                    substitute_dynamic_constants_in_constant(dc_a, dc_b, *field_id, edit)
+                    substitute_dynamic_constants_in_constant(substs, *field_id, edit)
                 })
                 .collect();
             if new_ty != ty || new_fields != fields {
@@ -161,8 +167,8 @@ pub(crate) fn substitute_dynamic_constants_in_constant(
             }
         }
         Constant::Summation(ty, idx, variant) => {
-            let new_ty = substitute_dynamic_constants_in_type(dc_a, dc_b, ty, edit);
-            let new_variant = substitute_dynamic_constants_in_constant(dc_a, dc_b, variant, edit);
+            let new_ty = substitute_dynamic_constants_in_type(substs, ty, edit);
+            let new_variant = substitute_dynamic_constants_in_constant(substs, variant, edit);
             if new_ty != ty || new_variant != variant {
                 edit.add_constant(Constant::Summation(new_ty, idx, new_variant))
             } else {
@@ -170,7 +176,7 @@ pub(crate) fn substitute_dynamic_constants_in_constant(
             }
         }
         Constant::Array(ty) => {
-            let new_ty = substitute_dynamic_constants_in_type(dc_a, dc_b, ty, edit);
+            let new_ty = substitute_dynamic_constants_in_type(substs, ty, edit);
             if new_ty != ty {
                 edit.add_constant(Constant::Array(new_ty))
             } else {
@@ -182,12 +188,10 @@ pub(crate) fn substitute_dynamic_constants_in_constant(
 }
 
 /*
- * Substitute all uses of a dynamic constant A with dynamic constant B in a
- * node.
+ * Substitute all uses of the dynamic constants specified by the subst map in a node.
  */
 pub(crate) fn substitute_dynamic_constants_in_node(
-    dc_a: DynamicConstantID,
-    dc_b: DynamicConstantID,
+    substs: &HashMap<DynamicConstantID, DynamicConstantID>,
     node: &mut Node,
     edit: &mut FunctionEdit,
 ) {
@@ -197,14 +201,14 @@ pub(crate) fn substitute_dynamic_constants_in_node(
             factors,
         } => {
             for factor in factors.into_iter() {
-                *factor = substitute_dynamic_constants(dc_a, dc_b, *factor, edit);
+                *factor = substitute_dynamic_constants(substs, *factor, edit);
             }
         }
         Node::Constant { id } => {
-            *id = substitute_dynamic_constants_in_constant(dc_a, dc_b, *id, edit);
+            *id = substitute_dynamic_constants_in_constant(substs, *id, edit);
         }
         Node::DynamicConstant { id } => {
-            *id = substitute_dynamic_constants(dc_a, dc_b, *id, edit);
+            *id = substitute_dynamic_constants(substs, *id, edit);
         }
         Node::Call {
             control: _,
@@ -213,7 +217,7 @@ pub(crate) fn substitute_dynamic_constants_in_node(
             args: _,
         } => {
             for dc_arg in dynamic_constants.into_iter() {
-                *dc_arg = substitute_dynamic_constants(dc_a, dc_b, *dc_arg, edit);
+                *dc_arg = substitute_dynamic_constants(substs, *dc_arg, edit);
             }
         }
         _ => {}
diff --git a/juno_samples/concat/src/main.rs b/juno_samples/concat/src/main.rs
index db3f37fd..8bcd7ba5 100644
--- a/juno_samples/concat/src/main.rs
+++ b/juno_samples/concat/src/main.rs
@@ -1,6 +1,7 @@
 #![feature(concat_idents)]
 
 use hercules_rt::runner;
+use hercules_rt::HerculesCPURef;
 
 juno_build::juno!("concat");
 
@@ -10,6 +11,21 @@ fn main() {
         let output = r.run(7).await;
         println!("{}", output);
         assert_eq!(output, 42);
+
+        const N: usize = 3;
+        let arr : Box<[i32]> = (2..=4).collect();
+        let arr = HerculesCPURef::from_slice(&arr);
+
+        let mut r = runner!(concat_switch);
+        let output = r.run(N as u64, 50, arr.clone()).await;
+        let result = output.as_slice::<i32>();
+        println!("{:?}", result);
+        assert_eq!(result, [0, 1, 2, 3, 4]);
+
+        let output = r.run(N as u64, 30, arr).await;
+        let result = output.as_slice::<i32>();
+        println!("{:?}", result);
+        assert_eq!(result, [2, 3, 4, 0, 1]);
     });
 }
 
-- 
GitLab


From 7c5a8b722b948d8f67f2e5a7c4f1364695baefd6 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 14:06:17 -0600
Subject: [PATCH 12/16] Clean-up formatting

---
 hercules_cg/src/cpu.rs                   |  48 +++++++---
 hercules_ir/src/dc_normalization.rs      | 112 +++++++++--------------
 hercules_ir/src/ir.rs                    |  21 +++--
 hercules_ir/src/typecheck.rs             |  59 +++++++-----
 hercules_opt/src/gcm.rs                  |   7 +-
 hercules_opt/src/inline.rs               |   6 +-
 hercules_opt/src/interprocedural_sroa.rs |  34 ++++---
 hercules_opt/src/utils.rs                |  28 ++++--
 8 files changed, 176 insertions(+), 139 deletions(-)

diff --git a/hercules_cg/src/cpu.rs b/hercules_cg/src/cpu.rs
index 94b8cc5d..23c53d66 100644
--- a/hercules_cg/src/cpu.rs
+++ b/hercules_cg/src/cpu.rs
@@ -583,9 +583,15 @@ impl<'a> CPUContext<'a> {
                     let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
                     let mut idx = 0;
                     while let Some(x) = xs.next() {
-                        let new_val = format!("%dc{}{}", dc.idx(),
-                            if xs.peek().is_some() { format!(".{}", idx) }
-                            else { "".to_string() });
+                        let new_val = format!(
+                            "%dc{}{}",
+                            dc.idx(),
+                            if xs.peek().is_some() {
+                                format!(".{}", idx)
+                            } else {
+                                "".to_string()
+                            }
+                        );
                         write!(
                             body,
                             "  {} = add i64{},%dc{}\n",
@@ -609,9 +615,15 @@ impl<'a> CPUContext<'a> {
                     let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
                     let mut idx = 0;
                     while let Some(x) = xs.next() {
-                        let new_val = format!("%dc{}{}", dc.idx(),
-                            if xs.peek().is_some() { format!(".{}", idx) }
-                            else { "".to_string() });
+                        let new_val = format!(
+                            "%dc{}{}",
+                            dc.idx(),
+                            if xs.peek().is_some() {
+                                format!(".{}", idx)
+                            } else {
+                                "".to_string()
+                            }
+                        );
                         write!(
                             body,
                             "  {} = mul i64{},%dc{}\n",
@@ -642,9 +654,15 @@ impl<'a> CPUContext<'a> {
                     let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
                     let mut idx = 0;
                     while let Some(x) = xs.next() {
-                        let new_val = format!("%dc{}{}", dc.idx(),
-                            if xs.peek().is_some() { format!(".{}", idx) }
-                            else { "".to_string() });
+                        let new_val = format!(
+                            "%dc{}{}",
+                            dc.idx(),
+                            if xs.peek().is_some() {
+                                format!(".{}", idx)
+                            } else {
+                                "".to_string()
+                            }
+                        );
                         write!(
                             body,
                             "  {} = call @llvm.umin.i64(i64{},i64%dc{}))\n",
@@ -661,9 +679,15 @@ impl<'a> CPUContext<'a> {
                     let mut cur_value = format!("%dc{}", xs.next().unwrap().idx());
                     let mut idx = 0;
                     while let Some(x) = xs.next() {
-                        let new_val = format!("%dc{}{}", dc.idx(),
-                            if xs.peek().is_some() { format!(".{}", idx) }
-                            else { "".to_string() });
+                        let new_val = format!(
+                            "%dc{}{}",
+                            dc.idx(),
+                            if xs.peek().is_some() {
+                                format!(".{}", idx)
+                            } else {
+                                "".to_string()
+                            }
+                        );
                         write!(
                             body,
                             "  {} = call @llvm.umax.i64(i64{},i64%dc{}))\n",
diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index ca76f3f7..e4f68f3d 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -1,33 +1,23 @@
 use crate::*;
 
-use std::cmp::{min, max};
+use std::cmp::{max, min};
 use std::ops::Deref;
 
 use either::Either;
 
-pub trait DynamicConstantView
-{
+pub trait DynamicConstantView {
     fn get_dynconst(&self, id: DynamicConstantID) -> impl Deref<Target = DynamicConstant> + '_;
     fn add_dynconst(&mut self, dc: DynamicConstant) -> DynamicConstantID;
 
-    fn dc_const(
-        &mut self,
-        val: usize,
-    ) -> DynamicConstantID {
+    fn dc_const(&mut self, val: usize) -> DynamicConstantID {
         self.add_dynconst(DynamicConstant::Constant(val))
     }
 
-    fn dc_param(
-        &mut self,
-        index: usize,
-    ) -> DynamicConstantID {
+    fn dc_param(&mut self, index: usize) -> DynamicConstantID {
         self.add_dynconst(DynamicConstant::Parameter(index))
     }
 
-    fn dc_add(
-        &mut self,
-        dcs: Vec<DynamicConstantID>,
-    ) -> DynamicConstantID {
+    fn dc_add(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
         let mut constant_val = 0;
         let mut fields = vec![];
 
@@ -53,10 +43,7 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_mul(
-        &mut self,
-        dcs: Vec<DynamicConstantID>,
-    ) -> DynamicConstantID {
+    fn dc_mul(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
         let mut constant_val = 1;
         let mut fields = vec![];
 
@@ -84,11 +71,8 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_min(
-        &mut self,
-        dcs: Vec<DynamicConstantID>,
-    ) -> DynamicConstantID {
-        let mut constant_val : Option<usize> = None;
+    fn dc_min(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
+        let mut constant_val: Option<usize> = None;
         let mut fields = vec![];
 
         for dc in dcs {
@@ -109,7 +93,10 @@ pub trait DynamicConstantView
             fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
         }
 
-        assert!(fields.len() > 0, "Min of 0 dynamic constant expressions is undefined");
+        assert!(
+            fields.len() > 0,
+            "Min of 0 dynamic constant expressions is undefined"
+        );
 
         if fields.len() <= 1 {
             fields[0]
@@ -119,11 +106,8 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_max(
-        &mut self,
-        dcs: Vec<DynamicConstantID>,
-    ) -> DynamicConstantID {
-        let mut constant_val : Option<usize> = None;
+    fn dc_max(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
+        let mut constant_val: Option<usize> = None;
         let mut fields = vec![];
 
         for dc in dcs {
@@ -144,7 +128,10 @@ pub trait DynamicConstantView
             fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
         }
 
-        assert!(fields.len() > 0, "Max of 0 dynamic constant expressions is undefined");
+        assert!(
+            fields.len() > 0,
+            "Max of 0 dynamic constant expressions is undefined"
+        );
 
         if fields.len() <= 1 {
             fields[0]
@@ -154,18 +141,14 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_sub(
-        &mut self,
-        x: DynamicConstantID,
-        y: DynamicConstantID,
-    ) -> DynamicConstantID {
-        let dc =
-            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
-                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-                    Either::Left(DynamicConstant::Constant(x - y)),
-                (_, DynamicConstant::Constant(0)) => Either::Right(x),
-                _ => Either::Left(DynamicConstant::Sub(x, y)),
-            };
+    fn dc_sub(&mut self, x: DynamicConstantID, y: DynamicConstantID) -> DynamicConstantID {
+        let dc = match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+            (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) => {
+                Either::Left(DynamicConstant::Constant(x - y))
+            }
+            (_, DynamicConstant::Constant(0)) => Either::Right(x),
+            _ => Either::Left(DynamicConstant::Sub(x, y)),
+        };
 
         match dc {
             Either::Left(dc) => self.add_dynconst(dc),
@@ -173,18 +156,14 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_div(
-        &mut self,
-        x: DynamicConstantID,
-        y: DynamicConstantID,
-    ) -> DynamicConstantID {
-        let dc =
-            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
-                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-                    Either::Left(DynamicConstant::Constant(x / y)),
-                (_, DynamicConstant::Constant(1)) => Either::Right(x),
-                _ => Either::Left(DynamicConstant::Div(x, y)),
-            };
+    fn dc_div(&mut self, x: DynamicConstantID, y: DynamicConstantID) -> DynamicConstantID {
+        let dc = match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+            (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) => {
+                Either::Left(DynamicConstant::Constant(x / y))
+            }
+            (_, DynamicConstant::Constant(1)) => Either::Right(x),
+            _ => Either::Left(DynamicConstant::Div(x, y)),
+        };
 
         match dc {
             Either::Left(dc) => self.add_dynconst(dc),
@@ -192,17 +171,13 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_rem(
-        &mut self,
-        x: DynamicConstantID,
-        y: DynamicConstantID,
-    ) -> DynamicConstantID {
-        let dc =
-            match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
-                (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) =>
-                    Either::Left(DynamicConstant::Constant(x % y)),
-                _ => Either::Left(DynamicConstant::Rem(x, y)),
-            };
+    fn dc_rem(&mut self, x: DynamicConstantID, y: DynamicConstantID) -> DynamicConstantID {
+        let dc = match (self.get_dynconst(x).deref(), self.get_dynconst(y).deref()) {
+            (DynamicConstant::Constant(x), DynamicConstant::Constant(y)) => {
+                Either::Left(DynamicConstant::Constant(x % y))
+            }
+            _ => Either::Left(DynamicConstant::Rem(x, y)),
+        };
 
         match dc {
             Either::Left(dc) => self.add_dynconst(dc),
@@ -210,10 +185,7 @@ pub trait DynamicConstantView
         }
     }
 
-    fn dc_normalize(
-        &mut self,
-        dc: DynamicConstant,
-    ) -> DynamicConstantID {
+    fn dc_normalize(&mut self, dc: DynamicConstant) -> DynamicConstantID {
         match dc {
             DynamicConstant::Add(xs) => self.dc_add(xs),
             DynamicConstant::Mul(xs) => self.dc_mul(xs),
diff --git a/hercules_ir/src/ir.rs b/hercules_ir/src/ir.rs
index ba8d607e..af9f0275 100644
--- a/hercules_ir/src/ir.rs
+++ b/hercules_ir/src/ir.rs
@@ -657,7 +657,10 @@ pub fn dynamic_constants_bottom_up(
                     // We have to yield the children of this node before
                     // this node itself. We keep track of which nodes have
                     // yielded using visited.
-                    if args.iter().any(|i| i.idx() >= visited.len() || invalid[i.idx()]) {
+                    if args
+                        .iter()
+                        .any(|i| i.idx() >= visited.len() || invalid[i.idx()])
+                    {
                         // This is an invalid dynamic constant and should be skipped
                         invalid.set(id.idx(), true);
                         continue;
@@ -1023,27 +1026,27 @@ impl DynamicConstant {
     pub fn add(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Add(vec![x, y])
     }
-    
+
     pub fn sub(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Sub(x, y)
     }
-    
+
     pub fn mul(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Mul(vec![x, y])
     }
-    
+
     pub fn div(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Div(x, y)
     }
-    
+
     pub fn rem(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Rem(x, y)
     }
-    
+
     pub fn min(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Min(vec![x, y])
     }
-    
+
     pub fn max(x: DynamicConstantID, y: DynamicConstantID) -> Self {
         Self::Max(vec![x, y])
     }
@@ -1087,7 +1090,9 @@ pub fn evaluate_dynamic_constant(
 ) -> Option<usize> {
     // Because of normalization, if a dynamic constant can be expressed as a constant it must be a
     // constant
-    let DynamicConstant::Constant(cons) = dcs[cons.idx()] else { return None; };
+    let DynamicConstant::Constant(cons) = dcs[cons.idx()] else {
+        return None;
+    };
     Some(cons)
 }
 
diff --git a/hercules_ir/src/typecheck.rs b/hercules_ir/src/typecheck.rs
index fdbbd46e..f7ea397e 100644
--- a/hercules_ir/src/typecheck.rs
+++ b/hercules_ir/src/typecheck.rs
@@ -190,9 +190,9 @@ fn typeflow(
             DynamicConstant::Add(xs)
             | DynamicConstant::Mul(xs)
             | DynamicConstant::Min(xs)
-            | DynamicConstant::Max(xs) => {
-                xs.iter().all(|dc| check_dynamic_constants(*dc, dynamic_constants, num_parameters))
-            }
+            | DynamicConstant::Max(xs) => xs
+                .iter()
+                .all(|dc| check_dynamic_constants(*dc, dynamic_constants, num_parameters)),
             DynamicConstant::Sub(x, y)
             | DynamicConstant::Div(x, y)
             | DynamicConstant::Rem(x, y) => {
@@ -736,7 +736,13 @@ fn typeflow(
             }
 
             // Construct the substitution object
-            let mut subst = DCSubst::new(types, reverse_type_map, dynamic_constants, reverse_dynamic_constant_map, dc_args);
+            let mut subst = DCSubst::new(
+                types,
+                reverse_type_map,
+                dynamic_constants,
+                reverse_dynamic_constant_map,
+                dc_args,
+            );
 
             // Check argument types.
             for (input, param_ty) in zip(inputs.iter().skip(1), callee.param_types.iter()) {
@@ -1115,10 +1121,7 @@ impl<'a> DCSubst<'a> {
         }
     }
 
-    fn intern_type(
-        &mut self,
-        ty: Type,
-    ) -> TypeID {
+    fn intern_type(&mut self, ty: Type) -> TypeID {
         if let Some(id) = self.reverse_type_map.get(&ty) {
             *id
         } else {
@@ -1129,10 +1132,7 @@ impl<'a> DCSubst<'a> {
         }
     }
 
-    fn type_subst(
-        &mut self,
-        typ: TypeID,
-    ) -> TypeID {
+    fn type_subst(&mut self, typ: TypeID) -> TypeID {
         match &self.types[typ.idx()] {
             Type::Control
             | Type::Boolean
@@ -1156,22 +1156,27 @@ impl<'a> DCSubst<'a> {
             }
             Type::Array(elem, dims) => {
                 let elem = *elem;
-                let new_dims = dims.clone().iter().map(|d| self.dyn_const_subst(*d)).collect();
+                let new_dims = dims
+                    .clone()
+                    .iter()
+                    .map(|d| self.dyn_const_subst(*d))
+                    .collect();
                 let new_elem = self.type_subst(elem);
                 self.intern_type(Type::Array(new_elem, new_dims))
             }
         }
     }
 
-    fn dyn_const_subst(
-        &mut self,
-        dyn_const: DynamicConstantID,
-    ) -> DynamicConstantID {
+    fn dyn_const_subst(&mut self, dyn_const: DynamicConstantID) -> DynamicConstantID {
         match &self.dynamic_constants[dyn_const.idx()] {
             DynamicConstant::Constant(_) => dyn_const,
             DynamicConstant::Parameter(i) => self.dc_args[*i],
             DynamicConstant::Add(xs) => {
-                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                let sxs = xs
+                    .clone()
+                    .into_iter()
+                    .map(|dc| self.dyn_const_subst(dc))
+                    .collect();
                 self.dc_add(sxs)
             }
             DynamicConstant::Sub(l, r) => {
@@ -1182,7 +1187,11 @@ impl<'a> DCSubst<'a> {
                 self.dc_sub(sx, sy)
             }
             DynamicConstant::Mul(xs) => {
-                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                let sxs = xs
+                    .clone()
+                    .into_iter()
+                    .map(|dc| self.dyn_const_subst(dc))
+                    .collect();
                 self.dc_mul(sxs)
             }
             DynamicConstant::Div(l, r) => {
@@ -1200,11 +1209,19 @@ impl<'a> DCSubst<'a> {
                 self.dc_rem(sx, sy)
             }
             DynamicConstant::Min(xs) => {
-                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                let sxs = xs
+                    .clone()
+                    .into_iter()
+                    .map(|dc| self.dyn_const_subst(dc))
+                    .collect();
                 self.dc_min(sxs)
             }
             DynamicConstant::Max(xs) => {
-                let sxs = xs.clone().into_iter().map(|dc| self.dyn_const_subst(dc)).collect();
+                let sxs = xs
+                    .clone()
+                    .into_iter()
+                    .map(|dc| self.dyn_const_subst(dc))
+                    .collect();
                 self.dc_max(sxs)
             }
         }
diff --git a/hercules_opt/src/gcm.rs b/hercules_opt/src/gcm.rs
index a22805b0..fe7c64a8 100644
--- a/hercules_opt/src/gcm.rs
+++ b/hercules_opt/src/gcm.rs
@@ -1167,8 +1167,11 @@ fn object_allocation(
                     args: _,
                 } => {
                     let dynamic_constants = dynamic_constants.to_vec();
-                    let dc_args = (0..dynamic_constants.len()).map(|i| edit.add_dynamic_constant(DynamicConstant::Parameter(i)));
-                    let substs = dc_args.zip(dynamic_constants.into_iter()).collect::<HashMap<_, _>>();
+                    let dc_args = (0..dynamic_constants.len())
+                        .map(|i| edit.add_dynamic_constant(DynamicConstant::Parameter(i)));
+                    let substs = dc_args
+                        .zip(dynamic_constants.into_iter())
+                        .collect::<HashMap<_, _>>();
 
                     for device in BACKED_DEVICES {
                         if let Some(mut callee_backing_size) = backing_allocations[&callee]
diff --git a/hercules_opt/src/inline.rs b/hercules_opt/src/inline.rs
index f63fa44c..848d957f 100644
--- a/hercules_opt/src/inline.rs
+++ b/hercules_opt/src/inline.rs
@@ -120,7 +120,11 @@ fn inline_func(
         // Assemble all the info we'll need to do the edit.
         let dcs_a = &dc_param_idx_to_dc_id[..dynamic_constants.len()];
         let dcs_b = dynamic_constants.to_vec();
-        let substs = dcs_a.iter().map(|i| *i).zip(dcs_b.into_iter()).collect::<HashMap<_, _>>();
+        let substs = dcs_a
+            .iter()
+            .map(|i| *i)
+            .zip(dcs_b.into_iter())
+            .collect::<HashMap<_, _>>();
         let args = args.clone();
         let old_num_nodes = editor.func().nodes.len();
         let old_id_to_new_id = |old_id: NodeID| NodeID::new(old_id.idx() + old_num_nodes);
diff --git a/hercules_opt/src/interprocedural_sroa.rs b/hercules_opt/src/interprocedural_sroa.rs
index 1797427e..f22c1fe8 100644
--- a/hercules_opt/src/interprocedural_sroa.rs
+++ b/hercules_opt/src/interprocedural_sroa.rs
@@ -316,17 +316,19 @@ fn compress_return_products(editors: &mut Vec<FunctionEditor>, all_callsites_edi
             let new_dcs = (*dynamic_constants).to_vec();
             let old_dcs = dc_param_idx_to_dc_id[..new_dcs.len()].to_vec();
             assert_eq!(old_dcs.len(), new_dcs.len());
-            let substs = old_dcs.into_iter().zip(new_dcs.into_iter()).collect::<HashMap<_, _>>();
+            let substs = old_dcs
+                .into_iter()
+                .zip(new_dcs.into_iter())
+                .collect::<HashMap<_, _>>();
 
             let edit_successful = editor.edit(|mut edit| {
                 let mut substituted = old_return_type_ids[function_id.idx()];
 
-                let substituted =
-                    substitute_dynamic_constants_in_type(
-                        &substs,
-                        old_return_type_ids[function_id.idx()],
-                        &mut edit,
-                    );
+                let substituted = substitute_dynamic_constants_in_type(
+                    &substs,
+                    old_return_type_ids[function_id.idx()],
+                    &mut edit,
+                );
 
                 let (expanded_product, readers) =
                     uncompress_product(&mut edit, &call_node_id, &substituted);
@@ -418,14 +420,16 @@ fn remove_return_singletons(editors: &mut Vec<FunctionEditor>, all_callsites_edi
                             edit.add_dynamic_constant(DynamicConstant::Parameter(param_idx))
                         })
                         .collect::<Vec<_>>();
-                    let substs = dc_params.into_iter().zip(dc_args.into_iter()).collect::<HashMap<_, _>>();
-
-                    let substituted =
-                        substitute_dynamic_constants_in_type(
-                            &substs,
-                            old_return_type_ids[function.idx()],
-                            &mut edit,
-                        );
+                    let substs = dc_params
+                        .into_iter()
+                        .zip(dc_args.into_iter())
+                        .collect::<HashMap<_, _>>();
+
+                    let substituted = substitute_dynamic_constants_in_type(
+                        &substs,
+                        old_return_type_ids[function.idx()],
+                        &mut edit,
+                    );
                     let empty_constant_id = edit.add_zero_constant(substituted);
                     let empty_node_id = edit.add_node(Node::Constant {
                         id: empty_constant_id,
diff --git a/hercules_opt/src/utils.rs b/hercules_opt/src/utils.rs
index 08dbd131..cc6bf444 100644
--- a/hercules_opt/src/utils.rs
+++ b/hercules_opt/src/utils.rs
@@ -32,9 +32,7 @@ pub(crate) fn substitute_dynamic_constants_in_type(
         Type::Summation(ref variants) => {
             let new_variants = variants
                 .into_iter()
-                .map(|variant_id| {
-                    substitute_dynamic_constants_in_type(substs, *variant_id, edit)
-                })
+                .map(|variant_id| substitute_dynamic_constants_in_type(substs, *variant_id, edit))
                 .collect();
             if new_variants != *variants {
                 edit.add_type(Type::Summation(new_variants))
@@ -79,7 +77,10 @@ pub(crate) fn substitute_dynamic_constants(
     match dc_clone {
         DynamicConstant::Constant(_) | DynamicConstant::Parameter(_) => dc,
         DynamicConstant::Add(xs) => {
-            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            let new_xs = xs
+                .iter()
+                .map(|x| substitute_dynamic_constants(substs, *x, edit))
+                .collect::<Vec<_>>();
             if new_xs != xs {
                 edit.add_dynamic_constant(DynamicConstant::Add(new_xs))
             } else {
@@ -96,7 +97,10 @@ pub(crate) fn substitute_dynamic_constants(
             }
         }
         DynamicConstant::Mul(xs) => {
-            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            let new_xs = xs
+                .iter()
+                .map(|x| substitute_dynamic_constants(substs, *x, edit))
+                .collect::<Vec<_>>();
             if new_xs != xs {
                 edit.add_dynamic_constant(DynamicConstant::Mul(new_xs))
             } else {
@@ -122,7 +126,10 @@ pub(crate) fn substitute_dynamic_constants(
             }
         }
         DynamicConstant::Min(xs) => {
-            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            let new_xs = xs
+                .iter()
+                .map(|x| substitute_dynamic_constants(substs, *x, edit))
+                .collect::<Vec<_>>();
             if new_xs != xs {
                 edit.add_dynamic_constant(DynamicConstant::Min(new_xs))
             } else {
@@ -130,7 +137,10 @@ pub(crate) fn substitute_dynamic_constants(
             }
         }
         DynamicConstant::Max(xs) => {
-            let new_xs = xs.iter().map(|x| substitute_dynamic_constants(substs, *x, edit)).collect::<Vec<_>>();
+            let new_xs = xs
+                .iter()
+                .map(|x| substitute_dynamic_constants(substs, *x, edit))
+                .collect::<Vec<_>>();
             if new_xs != xs {
                 edit.add_dynamic_constant(DynamicConstant::Max(new_xs))
             } else {
@@ -156,9 +166,7 @@ pub(crate) fn substitute_dynamic_constants_in_constant(
             let new_ty = substitute_dynamic_constants_in_type(substs, ty, edit);
             let new_fields = fields
                 .iter()
-                .map(|field_id| {
-                    substitute_dynamic_constants_in_constant(substs, *field_id, edit)
-                })
+                .map(|field_id| substitute_dynamic_constants_in_constant(substs, *field_id, edit))
                 .collect();
             if new_ty != ty || new_fields != fields {
                 edit.add_constant(Constant::Product(new_ty, new_fields))
-- 
GitLab


From 15a581b1cd205eca71679b007fe11e089327b3f8 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 14:13:11 -0600
Subject: [PATCH 13/16] Process min/max fields as a set

---
 hercules_ir/src/dc_normalization.rs | 33 +++++++++++++++++------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index e4f68f3d..06c45fea 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -1,6 +1,7 @@
 use crate::*;
 
 use std::cmp::{max, min};
+use std::collections::BTreeSet;
 use std::ops::Deref;
 
 use either::Either;
@@ -73,7 +74,9 @@ pub trait DynamicConstantView {
 
     fn dc_min(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
         let mut constant_val: Option<usize> = None;
-        let mut fields = vec![];
+        // For min and max we track the fields via a set during normalization as this removes
+        // duplicates (and we use a BTreeSet as it can produce its elements in sorted order)
+        let mut fields = BTreeSet::new();
 
         for dc in dcs {
             match self.get_dynconst(dc).deref() {
@@ -84,13 +87,15 @@ pub trait DynamicConstantView {
                         constant_val = Some(*x);
                     }
                 }
-                DynamicConstant::Min(xs) => fields.extend_from_slice(xs),
-                _ => fields.push(dc),
+                DynamicConstant::Min(xs) => fields.extend(xs),
+                _ => {
+                    fields.insert(dc);
+                }
             }
         }
 
         if let Some(const_val) = constant_val {
-            fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
+            fields.insert(self.add_dynconst(DynamicConstant::Constant(const_val)));
         }
 
         assert!(
@@ -99,16 +104,15 @@ pub trait DynamicConstantView {
         );
 
         if fields.len() <= 1 {
-            fields[0]
+            *fields.first().unwrap()
         } else {
-            fields.sort();
-            self.add_dynconst(DynamicConstant::Min(fields))
+            self.add_dynconst(DynamicConstant::Min(fields.into_iter().collect()))
         }
     }
 
     fn dc_max(&mut self, dcs: Vec<DynamicConstantID>) -> DynamicConstantID {
         let mut constant_val: Option<usize> = None;
-        let mut fields = vec![];
+        let mut fields = BTreeSet::new();
 
         for dc in dcs {
             match self.get_dynconst(dc).deref() {
@@ -119,13 +123,15 @@ pub trait DynamicConstantView {
                         constant_val = Some(*x);
                     }
                 }
-                DynamicConstant::Max(xs) => fields.extend_from_slice(xs),
-                _ => fields.push(dc),
+                DynamicConstant::Max(xs) => fields.extend(xs),
+                _ => {
+                    fields.insert(dc);
+                }
             }
         }
 
         if let Some(const_val) = constant_val {
-            fields.push(self.add_dynconst(DynamicConstant::Constant(const_val)));
+            fields.insert(self.add_dynconst(DynamicConstant::Constant(const_val)));
         }
 
         assert!(
@@ -134,10 +140,9 @@ pub trait DynamicConstantView {
         );
 
         if fields.len() <= 1 {
-            fields[0]
+            *fields.first().unwrap()
         } else {
-            fields.sort();
-            self.add_dynconst(DynamicConstant::Max(fields))
+            self.add_dynconst(DynamicConstant::Max(fields.into_iter().collect()))
         }
     }
 
-- 
GitLab


From 3af4013e02d472b692212f64bd229f432199d27c Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 20:27:17 -0600
Subject: [PATCH 14/16] Fixes to dynamic constant usage

---
 hercules_opt/src/fork_guard_elim.rs           | 29 +++++++------
 hercules_opt/src/forkify.rs                   |  3 +-
 .../hercules_interpreter/src/interpreter.rs   | 42 ++++++++++++-------
 3 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/hercules_opt/src/fork_guard_elim.rs b/hercules_opt/src/fork_guard_elim.rs
index 1abb8967..052fd0e4 100644
--- a/hercules_opt/src/fork_guard_elim.rs
+++ b/hercules_opt/src/fork_guard_elim.rs
@@ -1,4 +1,5 @@
 use std::collections::{HashMap, HashSet};
+use std::ops::Deref;
 
 use hercules_ir::*;
 
@@ -70,22 +71,24 @@ fn guarded_fork(
     };
 
     let mut factors = factors.iter().enumerate().map(|(idx, dc)| {
-        let DynamicConstant::Max(l, r) = *editor.get_dynamic_constant(*dc) else {
+        let factor = editor.get_dynamic_constant(*dc);
+        let DynamicConstant::Max(xs) = factor.deref() else {
             return Factor::Normal(*dc);
         };
 
-        // There really needs to be a better way to work w/ associativity.
-        let binding = [(l, r), (r, l)];
-        let id = binding.iter().find_map(|(a, b)| {
-            let DynamicConstant::Constant(1) = *editor.get_dynamic_constant(*a) else {
-                return None;
-            };
-            Some(b)
-        });
-
-        match id {
-            Some(v) => Factor::Max(idx, *v),
-            None => Factor::Normal(*dc),
+        // Filter out any terms which are just 1s
+        let non_ones = xs.iter().filter(|i| {
+            if let DynamicConstant::Constant(1) = editor.get_dynamic_constant(**i).deref() {
+                false
+            } else {
+                true
+            }
+        }).collect::<Vec<_>>();
+        // If we're left with just one term x, we had max { 1, x }
+        if non_ones.len() == 1 {
+            Factor::Max(idx, *non_ones[0])
+        } else {
+            Factor::Normal(*dc)
         }
     });
 
diff --git a/hercules_opt/src/forkify.rs b/hercules_opt/src/forkify.rs
index ce9ac141..ec4e9fbc 100644
--- a/hercules_opt/src/forkify.rs
+++ b/hercules_opt/src/forkify.rs
@@ -265,9 +265,8 @@ pub fn forkify_loop(
     let bound_dc_id = {
         let mut max_id = DynamicConstantID::new(0);
         editor.edit(|mut edit| {
-            // FIXME: Maybe add_dynamic_constant should intern?
             let one_id = edit.add_dynamic_constant(DynamicConstant::Constant(1));
-            max_id = edit.add_dynamic_constant(DynamicConstant::Max(one_id, bound_dc_id));
+            max_id = edit.add_dynamic_constant(DynamicConstant::max(one_id, bound_dc_id));
             Ok(edit)
         });
         max_id
diff --git a/hercules_test/hercules_interpreter/src/interpreter.rs b/hercules_test/hercules_interpreter/src/interpreter.rs
index a78330e4..871e304a 100644
--- a/hercules_test/hercules_interpreter/src/interpreter.rs
+++ b/hercules_test/hercules_interpreter/src/interpreter.rs
@@ -69,17 +69,17 @@ pub fn dyn_const_value(
     match dc {
         DynamicConstant::Constant(v) => *v,
         DynamicConstant::Parameter(v) => dyn_const_params[*v],
-        DynamicConstant::Add(a, b) => {
-            dyn_const_value(a, dyn_const_values, dyn_const_params)
-                + dyn_const_value(b, dyn_const_values, dyn_const_params)
+        DynamicConstant::Add(xs) => {
+            xs.iter().map(|x| dyn_const_value(x, dyn_const_values, dyn_const_params))
+                .fold(0, |s, v| s + v)
         }
         DynamicConstant::Sub(a, b) => {
             dyn_const_value(a, dyn_const_values, dyn_const_params)
                 - dyn_const_value(b, dyn_const_values, dyn_const_params)
         }
-        DynamicConstant::Mul(a, b) => {
-            dyn_const_value(a, dyn_const_values, dyn_const_params)
-                * dyn_const_value(b, dyn_const_values, dyn_const_params)
+        DynamicConstant::Mul(xs) => {
+            xs.iter().map(|x| dyn_const_value(x, dyn_const_values, dyn_const_params))
+                .fold(1, |p, v| p * v)
         }
         DynamicConstant::Div(a, b) => {
             dyn_const_value(a, dyn_const_values, dyn_const_params)
@@ -89,14 +89,28 @@ pub fn dyn_const_value(
             dyn_const_value(a, dyn_const_values, dyn_const_params)
                 % dyn_const_value(b, dyn_const_values, dyn_const_params)
         }
-        DynamicConstant::Max(a, b) => max(
-            dyn_const_value(a, dyn_const_values, dyn_const_params),
-            dyn_const_value(b, dyn_const_values, dyn_const_params),
-        ),
-        DynamicConstant::Min(a, b) => min(
-            dyn_const_value(a, dyn_const_values, dyn_const_params),
-            dyn_const_value(b, dyn_const_values, dyn_const_params),
-        ),
+        DynamicConstant::Max(xs) => {
+            xs.iter().map(|x| dyn_const_value(x, dyn_const_values, dyn_const_params))
+                .fold(None, |m, v| {
+                    if let Some(m) = m {
+                        Some(max(m, v))
+                    } else {
+                        Some(v)
+                    }
+                })
+                .unwrap()
+        }
+        DynamicConstant::Min(xs) => {
+            xs.iter().map(|x| dyn_const_value(x, dyn_const_values, dyn_const_params))
+                .fold(None, |m, v| {
+                    if let Some(m) = m {
+                        Some(min(m, v))
+                    } else {
+                        Some(v)
+                    }
+                })
+                .unwrap()
+        }
     }
 }
 
-- 
GitLab


From 73bcb5399c81aff683ecf136e3633c0e08386207 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 20:38:10 -0600
Subject: [PATCH 15/16] Clean-up Juno's dynamic constant generation

---
 juno_frontend/src/dynconst.rs | 113 +++++++++++++++-------------------
 1 file changed, 51 insertions(+), 62 deletions(-)

diff --git a/juno_frontend/src/dynconst.rs b/juno_frontend/src/dynconst.rs
index defab822..511dfa34 100644
--- a/juno_frontend/src/dynconst.rs
+++ b/juno_frontend/src/dynconst.rs
@@ -291,16 +291,16 @@ impl DynConst {
             .map(|(d, c)| self.build_mono(builder, d, c))
             .partition(|(_, neg)| !*neg);
 
-        let pos_sum = pos
-            .into_iter()
-            .map(|(t, _)| t)
-            .reduce(|x, y| builder.create_dynamic_constant_add(x, y))
-            .unwrap_or_else(|| builder.create_dynamic_constant_constant(0));
+        let pos_sum =
+            builder.create_dynamic_constant_add_many(pos.into_iter().map(|(t, _)| t).collect());
 
-        let neg_sum = neg
-            .into_iter()
-            .map(|(t, _)| t)
-            .reduce(|x, y| builder.create_dynamic_constant_add(x, y));
+        let neg_sum = if neg.is_empty() {
+            None
+        } else {
+            Some(
+                builder.create_dynamic_constant_add_many(neg.into_iter().map(|(t, _)| t).collect()),
+            )
+        };
 
         match neg_sum {
             None => pos_sum,
@@ -317,72 +317,61 @@ impl DynConst {
         term: &Vec<i64>,
         coeff: &Ratio<i64>,
     ) -> (DynamicConstantID, bool) {
-        let term_id = term
+        let (pos, neg): (Vec<_>, Vec<_>) = term
             .iter()
             .enumerate()
             .filter(|(_, p)| **p != 0)
             .map(|(v, p)| self.build_power(builder, v, *p))
-            .collect::<Vec<_>>()
-            .into_iter()
-            .reduce(|x, y| builder.create_dynamic_constant_mul(x, y));
-
-        match term_id {
-            None => {
-                // This means all powers of the term are 0, so we just
-                // output the coefficient
-                if !coeff.is_integer() {
-                    panic!("Dynamic constant is a non-integer constant")
-                } else {
-                    let val: i64 = coeff.to_integer();
-                    (
-                        builder.create_dynamic_constant_constant(val.abs() as usize),
-                        val < 0,
-                    )
-                }
-            }
-            Some(term) => {
-                if coeff.is_one() {
-                    (term, false)
-                } else {
-                    let numer: i64 = coeff.numer().abs();
-                    let denom: i64 = *coeff.denom(); // > 0
-
-                    let with_numer = if numer == 1 {
-                        term
-                    } else {
-                        let numer_id = builder.create_dynamic_constant_constant(numer as usize);
-                        builder.create_dynamic_constant_mul(numer_id, term)
-                    };
-                    let with_denom = if denom == 1 {
-                        with_numer
-                    } else {
-                        let denom_id = builder.create_dynamic_constant_constant(denom as usize);
-                        builder.create_dynamic_constant_div(with_numer, denom_id)
-                    };
-
-                    (with_denom, numer < 0)
-                }
+            .partition(|(_, neg)| !*neg);
+        let mut pos: Vec<_> = pos.into_iter().map(|(t, _)| t).collect();
+        let mut neg: Vec<_> = neg.into_iter().map(|(t, _)| t).collect();
+
+        let numerator = {
+            let numer: i64 = coeff.numer().abs();
+            let numer_dc = builder.create_dynamic_constant_constant(numer as usize);
+            pos.push(numer_dc);
+            builder.create_dynamic_constant_mul_many(pos)
+        };
+
+        let denominator = {
+            let denom: i64 = *coeff.denom();
+            assert!(denom > 0);
+
+            if neg.is_empty() && denom == 1 {
+                None
+            } else {
+                let denom_dc = builder.create_dynamic_constant_constant(denom as usize);
+                neg.push(denom_dc);
+                Some(builder.create_dynamic_constant_mul_many(neg))
             }
+        };
+
+        if let Some(denominator) = denominator {
+            (
+                builder.create_dynamic_constant_div(numerator, denominator),
+                *coeff.numer() < 0,
+            )
+        } else {
+            (numerator, *coeff.numer() < 0)
         }
     }
 
     // Build's a dynamic constant that is a certain power of a specific variable
-    fn build_power(&self, builder: &mut Builder, v: usize, power: i64) -> DynamicConstantID {
+    // Returns the dynamic constant id of variable raised to the absolute value of the power and a
+    // boolean indicating whether the power is actually negative
+    fn build_power(
+        &self,
+        builder: &mut Builder,
+        v: usize,
+        power: i64,
+    ) -> (DynamicConstantID, bool) {
         assert!(power != 0);
         let power_pos = power.abs() as usize;
 
         let var_id = builder.create_dynamic_constant_parameter(v);
-        let power_id = iter::repeat(var_id)
-            .take(power_pos)
-            .map(|_| var_id)
-            .reduce(|x, y| builder.create_dynamic_constant_mul(x, y))
-            .expect("Power is non-zero");
+        let power_id =
+            builder.create_dynamic_constant_mul_many((0..power_pos).map(|_| var_id).collect());
 
-        if power > 0 {
-            power_id
-        } else {
-            let one_id = builder.create_dynamic_constant_constant(1);
-            builder.create_dynamic_constant_div(one_id, power_id)
-        }
+        (power_id, power < 0)
     }
 }
-- 
GitLab


From 6d7f0a764816e01656f24f0efd42da06ecd15788 Mon Sep 17 00:00:00 2001
From: Aaron Councilman <aaronjc4@illinois.edu>
Date: Thu, 30 Jan 2025 21:49:01 -0600
Subject: [PATCH 16/16] Normalize min of zero

---
 hercules_ir/src/dc_normalization.rs | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/hercules_ir/src/dc_normalization.rs b/hercules_ir/src/dc_normalization.rs
index 06c45fea..e9f8f23a 100644
--- a/hercules_ir/src/dc_normalization.rs
+++ b/hercules_ir/src/dc_normalization.rs
@@ -95,15 +95,16 @@ pub trait DynamicConstantView {
         }
 
         if let Some(const_val) = constant_val {
-            fields.insert(self.add_dynconst(DynamicConstant::Constant(const_val)));
+            // Since dynamic constants are non-negative, ignore the constant if it is 0
+            if const_val != 0 {
+                fields.insert(self.add_dynconst(DynamicConstant::Constant(const_val)));
+            }
         }
 
-        assert!(
-            fields.len() > 0,
-            "Min of 0 dynamic constant expressions is undefined"
-        );
-
-        if fields.len() <= 1 {
+        if fields.len() == 0 {
+            // The minimum of 0 dynamic constants is 0 since dynamic constants are non-negative
+            self.add_dynconst(DynamicConstant::Constant(0))
+        } else if fields.len() <= 1 {
             *fields.first().unwrap()
         } else {
             self.add_dynconst(DynamicConstant::Min(fields.into_iter().collect()))
-- 
GitLab