From e8ee780c6749920769d16e8d0f9310da05b69a3a Mon Sep 17 00:00:00 2001
From: Xavier Routh <xrouth2@illinois.edu>
Date: Fri, 28 Feb 2025 09:57:09 -0600
Subject: [PATCH 1/3] loop bound canon lte

---
 hercules_opt/src/loop_bound_canon.rs      | 130 ++++++++++++++++------
 juno_samples/rodinia/backprop/src/cpu.sch |  10 ++
 2 files changed, 107 insertions(+), 33 deletions(-)

diff --git a/hercules_opt/src/loop_bound_canon.rs b/hercules_opt/src/loop_bound_canon.rs
index edda6b63..126f33cf 100644
--- a/hercules_opt/src/loop_bound_canon.rs
+++ b/hercules_opt/src/loop_bound_canon.rs
@@ -73,6 +73,9 @@ pub fn canonicalize_single_loop_bounds(
         .into_iter()
         .partition(|f| loop_bound_iv_phis.contains(&f.phi()));
 
+    println!("{:?}", loop_bound_ivs);
+
+
     // Assume there is only one loop bound iv.
     if loop_bound_ivs.len() != 1 {
         return false;
@@ -93,9 +96,6 @@ pub fn canonicalize_single_loop_bounds(
         return false;
     };
 
-    let Some(final_value) = final_value else {
-        return false;
-    };
 
     let Some(loop_pred) = editor
         .get_uses(l.header)
@@ -109,8 +109,23 @@ pub fn canonicalize_single_loop_bounds(
 
     // (init_id, bound_id, binop node, if node).
 
+    // FIXME: This is not always correct, depends on lots of things about the loop IV. 
+    let loop_bound_dc = match *editor.node(condition_node) {
+        Node::Binary { left, right, op } => match op {
+            BinaryOperator::LT => right,
+            BinaryOperator::LTE => right,
+            BinaryOperator::GT => {return false}
+            BinaryOperator::GTE => {return false}
+            BinaryOperator::EQ => {return false}
+            BinaryOperator::NE => {return false}
+            _ => {return false}
+        },
+        _ => {return false}
+    };
+
+    
     // FIXME: This is quite fragile.
-    let guard_info: Option<(NodeID, NodeID, NodeID, NodeID)> = (|| {
+    let mut guard_info: Option<(NodeID, NodeID, NodeID, NodeID)> = (|| {
         let Node::ControlProjection {
             control,
             selection: _,
@@ -119,7 +134,7 @@ pub fn canonicalize_single_loop_bounds(
             return None;
         };
 
-        let Node::If { control, cond } = editor.node(control) else {
+        let Node::If { cond, ..} = editor.node(control) else {
             return None;
         };
 
@@ -129,7 +144,7 @@ pub fn canonicalize_single_loop_bounds(
 
         let Node::Binary {
             left: _,
-            right: _,
+            right: r,
             op: loop_op,
         } = editor.node(condition_node)
         else {
@@ -144,7 +159,7 @@ pub fn canonicalize_single_loop_bounds(
             return None;
         }
 
-        if right != final_value {
+        if right != r {
             return None;
         }
 
@@ -169,7 +184,7 @@ pub fn canonicalize_single_loop_bounds(
     // We are assuming this is a simple loop bound (i.e only one induction variable involved), so that .
     let Node::DynamicConstant {
         id: loop_bound_dc_id,
-    } = *editor.node(final_value)
+    } = *editor.node(loop_bound_dc)
     else {
         return false;
     };
@@ -177,9 +192,9 @@ pub fn canonicalize_single_loop_bounds(
     // We need to do 4 (5) things, which are mostly separate.
 
     // 0) Make the update into addition.
-    // 1) Make the update a positive value.
-    // 2) Transform the condition into a `<`
-    // 3) Adjust update to be 1 (and bounds).
+    // 1) Adjust update to be 1 (and bounds).
+    // 2) Make the update a positive value. / Transform the condition into a `<`
+    // - Are these separate?
     // 4) Change init to start from 0.
 
     // 5) Find some way to get fork-guard-elim to work with the new fork.
@@ -198,7 +213,13 @@ pub fn canonicalize_single_loop_bounds(
                     return false;
                 }
             }
-            BinaryOperator::LTE => todo!(),
+            BinaryOperator::LTE => {
+                if left == *update_expression && editor.node(right).is_dynamic_constant() {
+                    right
+                } else {
+                    return false;
+                }
+            }
             BinaryOperator::GT => todo!(),
             BinaryOperator::GTE => todo!(),
             BinaryOperator::EQ => todo!(),
@@ -211,8 +232,10 @@ pub fn canonicalize_single_loop_bounds(
         _ => return false,
     };
 
+    let condition_node_data = editor.node(condition_node).clone();
+
     let Node::DynamicConstant {
-        id: bound_node_dc_id,
+        id: mut bound_node_dc_id,
     } = *editor.node(dc_bound_node)
     else {
         return false;
@@ -220,7 +243,59 @@ pub fn canonicalize_single_loop_bounds(
 
     // If increment is negative (how in the world do we know that...)
     // Increment can be DefinetlyPostiive, Unknown, DefinetlyNegative.
+    let misc_guard_thing: Option<Node> =  if let Some((init_id, bound_id, binop_node, if_node)) = guard_info {
+        Some(editor.node(binop_node).clone())
+    } else {
+        None
+    };
+    println!("condition node: {:?}", condition_node);
+    let users = editor.get_users(condition_node).collect_vec(); 
+    println!("{:?}", users);
+
+    let mut condition_node = condition_node;
+
+    let result = editor.edit(|mut edit| {
+        // 2) Transform the condition into a < (from <=)
+        if let Node::Binary { left, right, op } = condition_node_data {
+            if BinaryOperator::LTE == op && left == *update_expression {
+                // Change the condition into <
+                let new_bop = edit.add_node(Node::Binary { left, right, op: BinaryOperator::LT });
+                
+                // Change the bound dc to be bound_dc + 1
+                let one = DynamicConstant::Constant(1);
+                let one = edit.add_dynamic_constant(one);
+
+                let tmp = DynamicConstant::add(bound_node_dc_id, one);
+                let new_condition_dc = edit.add_dynamic_constant(tmp);
+
+                let new_dc_bound_node = edit.add_node(Node::DynamicConstant { id: new_condition_dc });
+
+                // // 5) Change loop guard:
+                guard_info = if let Some((init_id, bound_id, binop_node, if_node)) = guard_info {
+                    // Change binop node
+                    let Some(Node::Binary { left, right, op }) =  misc_guard_thing else {unreachable!()};
+                    let blah = edit.add_node(Node::DynamicConstant { id:  new_condition_dc});
+
+                    // FIXME: Don't assume that right is the loop bound in the guard. 
+                    let new_binop_node = edit.add_node(Node::Binary { left, right: blah, op: BinaryOperator::LT });
+
+                    edit = edit.replace_all_uses_where(binop_node, new_binop_node, |usee| *usee == if_node)?;
+                    Some((init_id, bound_id, new_binop_node, if_node))
+                } else {guard_info};
+
+                edit = edit.replace_all_uses_where(dc_bound_node, new_dc_bound_node, |usee| *usee == new_bop)?;
+                edit = edit.replace_all_uses(condition_node, new_bop)?;
+
+                // Change loop condition
+                dc_bound_node = new_dc_bound_node;
+                bound_node_dc_id = new_condition_dc;
+                condition_node = new_bop;
+            }
+        };
+        Ok(edit)
+    });
 
+    
     let update_expr_users: Vec<_> = editor
         .get_users(*update_expression)
         .filter(|node| *node != iv.phi() && *node != condition_node)
@@ -241,34 +316,23 @@ pub fn canonicalize_single_loop_bounds(
         let new_init = edit.add_node(new_init);
         edit = edit.replace_all_uses_where(*initializer, new_init, |usee| *usee == iv.phi())?;
 
-        let new_condition_id = DynamicConstant::sub(bound_node_dc_id, init_dc_id);
-        let new_condition = Node::DynamicConstant {
-            id: edit.add_dynamic_constant(new_condition_id),
+        let new_condition_dc = DynamicConstant::sub(bound_node_dc_id, init_dc_id);
+        let new_condition_dc_id = Node::DynamicConstant {
+            id: edit.add_dynamic_constant(new_condition_dc),
         };
-        let new_condition = edit.add_node(new_condition);
+        let new_condition_dc = edit.add_node(new_condition_dc_id);
         edit = edit
-            .replace_all_uses_where(dc_bound_node, new_condition, |usee| *usee == condition_node)?;
+            .replace_all_uses_where(dc_bound_node, new_condition_dc, |usee| *usee == condition_node)?;
 
-        // Change loop guard:
+        // 5) Change loop guard:
         if let Some((init_id, bound_id, binop_node, if_node)) = guard_info {
             edit = edit.replace_all_uses_where(init_id, new_init, |usee| *usee == binop_node)?;
             edit =
-                edit.replace_all_uses_where(bound_id, new_condition, |usee| *usee == binop_node)?;
+                edit.replace_all_uses_where(bound_id, new_condition_dc, |usee| *usee == binop_node)?;
         }
+        
 
-        // for user in update_expr_users {
-        //     let new_user = Node::Binary {
-        //         left: user,
-        //         right: *initializer,
-        //         op: BinaryOperator::Add,
-        //     };
-        //     let new_user = edit.add_node(new_user);
-        //     edit = edit.replace_all_uses(user, new_user)?;
-        // }
-
-        // for
-
-        // Add the offset back to users of the IV update expression
+        // 4) Add the offset back to users of the IV update expression
         let new_user = Node::Binary {
             left: *update_expression,
             right: *initializer,
diff --git a/juno_samples/rodinia/backprop/src/cpu.sch b/juno_samples/rodinia/backprop/src/cpu.sch
index fa3dccf1..d1fe8953 100644
--- a/juno_samples/rodinia/backprop/src/cpu.sch
+++ b/juno_samples/rodinia/backprop/src/cpu.sch
@@ -15,10 +15,20 @@ delete-uncalled(*);
 no-memset(layer_forward@res);
 lift-dc-math(*);
 loop-bound-canon(*);
+dce(*);
+lift-dc-math(*);
 fixpoint {
   forkify(*);
   fork-guard-elim(*);
   fork-coalesce(*);
 }
 
+fork-split(*);
+gvn(*);
+phi-elim(*);
+dce(*);
+unforkify(*);
+gvn(*);
+phi-elim(*);
+dce(*);
 gcm(*);
-- 
GitLab


From 6f161d1e06f48be09841d17ce0eddfd6c0f80bc7 Mon Sep 17 00:00:00 2001
From: Xavier Routh <xrouth2@illinois.edu>
Date: Fri, 28 Feb 2025 16:40:22 -0600
Subject: [PATCH 2/3] canon + forkify fix

---
 hercules_opt/src/forkify.rs               | 21 ++++++++++++++++-----
 hercules_opt/src/loop_bound_canon.rs      |  8 ++++----
 juno_samples/rodinia/backprop/src/cpu.sch |  1 +
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/hercules_opt/src/forkify.rs b/hercules_opt/src/forkify.rs
index a6308d66..40fda1ad 100644
--- a/hercules_opt/src/forkify.rs
+++ b/hercules_opt/src/forkify.rs
@@ -95,6 +95,8 @@ pub fn forkify_loop(
 ) -> bool {
     let function = editor.func();
 
+    println!("forkifying {:?}", l.header);
+
     let Some(loop_condition) = get_loop_exit_conditions(function, l, control_subgraph) else {
         return false;
     };
@@ -106,12 +108,13 @@ pub fn forkify_loop(
     else {
         return false;
     };
-
+    
     // Compute loop variance
     let loop_variance = compute_loop_variance(editor, l);
     let ivs = compute_induction_vars(editor.func(), l, &loop_variance);
     let ivs = compute_iv_ranges(editor, l, ivs, &loop_condition);
     let Some(canonical_iv) = has_canonical_iv(editor, l, &ivs) else {
+        println!("no canonical iv");
         return false;
     };
 
@@ -130,6 +133,7 @@ pub fn forkify_loop(
     };
 
     let Some(bound_dc_id) = bound else {
+        println!("no bound iv");
         return false;
     };
 
@@ -167,6 +171,8 @@ pub fn forkify_loop(
         return false;
     }
 
+    println!("this one");
+
     // Get all phis used outside of the loop, they need to be reductionable.
     // For now just assume all phis will be phis used outside of the loop, except for the canonical iv.
     // FIXME: We need a different definiton of `loop_nodes` to check for phis used outside hte loop than the one
@@ -196,6 +202,9 @@ pub fn forkify_loop(
 
     let loop_body_last = editor.get_uses(loop_if).next().unwrap();
 
+
+    println!("phis {:?}", reductionable_phis);
+
     if reductionable_phis
         .iter()
         .any(|phi| !matches!(phi, LoopPHI::Reductionable { .. }))
@@ -403,6 +412,7 @@ pub fn forkify_loop(
         Ok(edit)
     });
 
+    println!("result: {:?}", result);
     return result;
 }
 
@@ -530,10 +540,11 @@ pub fn analyze_phis<'a>(
         let intersection: HashSet<_> = set1.intersection(&set2).cloned().collect();
 
         // If this phi uses any other phis the node is loop dependant,
-        // we use `phis` because this phi can actually contain the loop iv and its fine.
-        if uses_for_dependance.any(|node| phis.contains(&node) && node != *phi) {
-            LoopPHI::LoopDependant(*phi)
-        } else if intersection.clone().iter().next().is_some() {
+        // // we use `phis` because this phi can actually contain the loop iv and its fine.
+        // if uses_for_dependance.any(|node| phis.contains(&node) && node != *phi) {
+        //     LoopPHI::LoopDependant(*phi)
+        // } else 
+        if intersection.clone().iter().next().is_some() {
             // PHIs on the frontier of the uses by the candidate phi, i.e in uses_for_dependance need
             // to have headers that postdominate the loop continue latch. The value of the PHI used needs to be defined
             // by the time the reduce is triggered (at the end of the loop's internal control).
diff --git a/hercules_opt/src/loop_bound_canon.rs b/hercules_opt/src/loop_bound_canon.rs
index 126f33cf..203e45f8 100644
--- a/hercules_opt/src/loop_bound_canon.rs
+++ b/hercules_opt/src/loop_bound_canon.rs
@@ -73,7 +73,7 @@ pub fn canonicalize_single_loop_bounds(
         .into_iter()
         .partition(|f| loop_bound_iv_phis.contains(&f.phi()));
 
-    println!("{:?}", loop_bound_ivs);
+    // println!("{:?}", loop_bound_ivs);
 
 
     // Assume there is only one loop bound iv.
@@ -248,9 +248,9 @@ pub fn canonicalize_single_loop_bounds(
     } else {
         None
     };
-    println!("condition node: {:?}", condition_node);
+    // println!("condition node: {:?}", condition_node);
     let users = editor.get_users(condition_node).collect_vec(); 
-    println!("{:?}", users);
+    // println!("{:?}", users);
 
     let mut condition_node = condition_node;
 
@@ -280,7 +280,7 @@ pub fn canonicalize_single_loop_bounds(
                     let new_binop_node = edit.add_node(Node::Binary { left, right: blah, op: BinaryOperator::LT });
 
                     edit = edit.replace_all_uses_where(binop_node, new_binop_node, |usee| *usee == if_node)?;
-                    Some((init_id, bound_id, new_binop_node, if_node))
+                    Some((init_id, blah, new_binop_node, if_node))
                 } else {guard_info};
 
                 edit = edit.replace_all_uses_where(dc_bound_node, new_dc_bound_node, |usee| *usee == new_bop)?;
diff --git a/juno_samples/rodinia/backprop/src/cpu.sch b/juno_samples/rodinia/backprop/src/cpu.sch
index d1fe8953..43294807 100644
--- a/juno_samples/rodinia/backprop/src/cpu.sch
+++ b/juno_samples/rodinia/backprop/src/cpu.sch
@@ -17,6 +17,7 @@ lift-dc-math(*);
 loop-bound-canon(*);
 dce(*);
 lift-dc-math(*);
+
 fixpoint {
   forkify(*);
   fork-guard-elim(*);
-- 
GitLab


From 312560ba091f341cfdc4fe375a2ffa47ab9a1ee9 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Sun, 2 Mar 2025 16:39:47 -0600
Subject: [PATCH 3/3] cleanup

---
 hercules_opt/src/forkify.rs | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/hercules_opt/src/forkify.rs b/hercules_opt/src/forkify.rs
index 40fda1ad..189670de 100644
--- a/hercules_opt/src/forkify.rs
+++ b/hercules_opt/src/forkify.rs
@@ -95,8 +95,6 @@ pub fn forkify_loop(
 ) -> bool {
     let function = editor.func();
 
-    println!("forkifying {:?}", l.header);
-
     let Some(loop_condition) = get_loop_exit_conditions(function, l, control_subgraph) else {
         return false;
     };
@@ -114,7 +112,6 @@ pub fn forkify_loop(
     let ivs = compute_induction_vars(editor.func(), l, &loop_variance);
     let ivs = compute_iv_ranges(editor, l, ivs, &loop_condition);
     let Some(canonical_iv) = has_canonical_iv(editor, l, &ivs) else {
-        println!("no canonical iv");
         return false;
     };
 
@@ -133,7 +130,6 @@ pub fn forkify_loop(
     };
 
     let Some(bound_dc_id) = bound else {
-        println!("no bound iv");
         return false;
     };
 
@@ -171,8 +167,6 @@ pub fn forkify_loop(
         return false;
     }
 
-    println!("this one");
-
     // Get all phis used outside of the loop, they need to be reductionable.
     // For now just assume all phis will be phis used outside of the loop, except for the canonical iv.
     // FIXME: We need a different definiton of `loop_nodes` to check for phis used outside hte loop than the one
@@ -202,9 +196,6 @@ pub fn forkify_loop(
 
     let loop_body_last = editor.get_uses(loop_if).next().unwrap();
 
-
-    println!("phis {:?}", reductionable_phis);
-
     if reductionable_phis
         .iter()
         .any(|phi| !matches!(phi, LoopPHI::Reductionable { .. }))
@@ -412,7 +403,6 @@ pub fn forkify_loop(
         Ok(edit)
     });
 
-    println!("result: {:?}", result);
     return result;
 }
 
-- 
GitLab