From ae334572d2a178505665be0943c2be5891045ff4 Mon Sep 17 00:00:00 2001
From: Russel Arbore <russel.jma@gmail.com>
Date: Thu, 30 Jan 2025 10:01:51 -0600
Subject: [PATCH] remove loop-canon

---
 hercules_opt/src/loop_canonicalization.rs     | 899 ------------------
 hercules_test/hercules_interpreter/src/lib.rs |   2 -
 .../tests/fork_transform_tests.rs             |   1 -
 .../hercules_tests/tests/forkify_tests.rs     |   1 -
 .../hercules_tests/tests/interpreter_tests.rs |   1 -
 .../hercules_tests/tests/loop_tests.rs        |   2 -
 juno_scheduler/src/compile.rs                 |   3 -
 juno_scheduler/src/ir.rs                      |   1 -
 juno_scheduler/src/pm.rs                      |  27 -
 9 files changed, 937 deletions(-)
 delete mode 100644 hercules_opt/src/loop_canonicalization.rs

diff --git a/hercules_opt/src/loop_canonicalization.rs b/hercules_opt/src/loop_canonicalization.rs
deleted file mode 100644
index 12d8fd3b..00000000
--- a/hercules_opt/src/loop_canonicalization.rs
+++ /dev/null
@@ -1,899 +0,0 @@
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::iter::FromIterator;
-
-use hercules_ir::Constant;
-use hercules_ir::TypeID;
-
-use nestify::nest;
-
-use hercules_ir::get_uses;
-
-use itertools::Itertools;
-
-use hercules_ir::BinaryOperator;
-
-use hercules_ir::Function;
-use hercules_ir::Node;
-
-use hercules_ir::ID;
-
-use hercules_ir::NodeID;
-
-use hercules_ir::Subgraph;
-
-use crate::calculate_loop_nodes;
-use crate::compute_loop_variance;
-use crate::get_loop_exit_conditions;
-use crate::BasicInductionVariable;
-use crate::FunctionEditor;
-use crate::Loop;
-use crate::LoopExit;
-use crate::LoopVariance;
-use crate::LoopVarianceInfo;
-
-use hercules_ir::LoopTree;
-
-/** On return `true` means the function has been modified, and loop_canonicalization can be ran again 
-   (with newly analysis info), to canonicalze more loops. */
-pub fn loop_canonicalization(
-    editor: &mut FunctionEditor,
-    control_subgraph: &Subgraph,
-    fork_join_map: &HashMap<NodeID, NodeID>,
-    loops: &LoopTree,
-    typing: &Vec<TypeID>,
-) -> bool {
-
-    let natural_loops = loops
-        .bottom_up_loops()
-        .into_iter()
-        .filter(|(k, _)| editor.func().nodes[k.idx()].is_region());
-
-    let natural_loops: Vec<_> = natural_loops.collect();
-
-    let mut loop_exits = HashMap::new();
-
-    // FIXME: Add return type enum of: {transformed, already in transformed form (not modified), unable to transform}.
-    for l in &natural_loops {
-        let Some(loop_exit) = get_loop_exit_conditions(
-            editor.func(),
-            &Loop {
-                header: l.0,
-                control: l.1.clone(),
-            },
-            control_subgraph,
-        ) else {
-            continue;
-        };
-        loop_exits.insert(l.0, loop_exit);
-    }
-
-    for l in natural_loops {
-        let natural_loop = &Loop {
-            header: l.0,
-            control: l.1.clone(),
-        };
-        if canonicalize_loop(
-            editor,
-            loop_exits.get(&l.0).copied(),
-            fork_join_map,
-            natural_loop,
-            typing,
-        ) {
-            let nodes = &editor.func().nodes;
-            let mut xuser = NodeID::new(0);
-            let mut xother_user = NodeID::new(0);
-            for id in editor.node_ids() {
-                if nodes[id.idx()].is_region() {
-                    for user in editor.get_users(id) {
-                        if let Node::Phi {
-                            control: _,
-                            ref data,
-                        } = nodes[user.idx()]
-                            && data.into_iter().any(|id| nodes[id.idx()].is_undef())
-                        {
-                            for other_user in editor.get_users(id) {
-                                if let Node::Phi {
-                                    control: _,
-                                    data: ref other_data,
-                                } = nodes[other_user.idx()]
-                                    && data.into_iter().zip(other_data.into_iter()).all(
-                                        |(datum, other_datum)| {
-                                            datum == other_datum || nodes[datum.idx()].is_undef()
-                                        },
-                                    )
-                                    && user != other_user
-                                {
-                                    xuser = user;
-                                    xother_user = other_user;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            if xuser.idx() != 0 && xother_user.idx() != 0 {
-                editor.edit(|mut edit| {
-                    edit = edit.replace_all_uses(xuser, xother_user)?;
-                    edit.delete_node(xuser)
-                });
-            }
-
-            return true;
-        }
-    }
-
-    if merge_phis(editor) {
-        return true;
-    }
-
-    return false;
-}
-
-
-
-/** 
- * Replaces undef's in PHIs to use already existing PHIs. 
- */
-pub fn merge_phis(editor: &mut FunctionEditor) -> bool {
-    
-    let mut changed = false;
-    let mut worklist: Vec<NodeID> = editor.node_ids().filter(|node| editor.func().nodes[node.idx()].is_phi()).collect();
-
-
-    while let Some(phi) = worklist.pop() {
-        let Node::Phi { control: phi_region, data: phi_data } = &editor.func().nodes[phi.idx()] else {unreachable!()};
-
-        // undef_idx
-        // FIXME: Enumerate + Partition
-        let undefs: Vec<_> = phi_data.iter().positions(|usee| editor.func().nodes[usee.idx()].is_undef()).collect();
-        let non_undefs: Vec<_> = phi_data.iter().positions(|usee| !editor.func().nodes[usee.idx()].is_undef()).collect();
-
-        if undefs.is_empty() {
-            continue;
-        }
-
-        if non_undefs.is_empty() {
-            continue;
-        }
-        
-        // Try to merge with other phis of the same region
-        let candidate = editor.get_users(*phi_region).filter(|node| editor.func().nodes[node.idx()].is_phi());
-
-        let mut merge_candidates = candidate.filter(|node| {
-            if phi == *node {
-                return false; 
-            }
-
-            if let Node::Phi { control: candidate_region, data: candidate_data } = &editor.func().nodes[node.idx()] {
-
-                // Regions have to match
-                if candidate_region != phi_region {
-                    return false;
-                }
-
-                // FIXME: Sort by candidate that can replace the most undefs.
-                // All undefs need to have data. 
-                if undefs.iter().any(|idx| editor.func().nodes[candidate_data[*idx].idx()].is_undef()) {
-                    return false;
-                }
-
-                // All non_undefs need to be the same. 
-                if non_undefs.iter().any(|idx| candidate_data[*idx] != phi_data[*idx]) {
-                    return false;
-                }
-                true
-            } else {
-                false
-            }
-        });
-
-
-        let Some(data) = merge_candidates.next() else {continue};
-        drop(merge_candidates);
-        
-        editor.edit(|mut edit|{
-            let edit = edit.replace_all_uses(phi, data)?;
-            edit.delete_node(phi)
-        });
-        changed = true;
-        
-    }
-    changed   
-}
-
-/** 
-  
- */
-pub fn canonicalize_loop(
-    editor: &mut FunctionEditor,
-    loop_exit: Option<LoopExit>,
-    fork_join_map: &HashMap<NodeID, NodeID>,
-    natural_loop: &Loop,
-    typing: &Vec<TypeID>
-) -> bool {
-
-    let Some(loop_condition) = loop_exit else {return false};
-    let LoopExit::Conditional { if_node: loop_if, condition_node } = loop_condition.clone() else {return false};
-
-    // let Some((iv_expression, base_iv)) = has_alternate_bounds(editor.func(), 
-    //     natural_loop, condition_node, &basic_ivs, loop_variance) 
-    // else {return false};
-
-    // Find nodes that are `in the loop` 
-    // - used by a phi (or the loop region)
-    // - uses a phi (the loop region)
-    // All other nodes are 'out of the loop'
-    // All edges from the loop to out of the loop need to have a phi added, 
-    // controlled by the loop header. The loop entry edge is undef, the loop continued data node is 
-    // the edge it is being inserted in. 
-    // 
-    // Inner control needs to be moved, with PHIs being inserted as appropriate for now undef'd variables.
-
-    let loop_nodes = calculate_loop_nodes(editor, natural_loop);
-    
-    let header_initial_idx = editor.get_uses(natural_loop.header)
-        .position(|node| !natural_loop.control[node.idx()]  // Position of the predecessor (used by header but not in loop body.)
-    ).unwrap();
-
-    let header_continue_idx = editor.get_uses(natural_loop.header)
-        .position(|node| natural_loop.control[node.idx()]  
-    ).unwrap();
-
-
-    // Check loop variables that are used by smthn outside the loop.
-    let binding = loop_nodes.clone();
-    let phis_to_add: Vec<NodeID> = binding.iter()
-        .filter(
-        |loop_node| !editor.func().nodes[loop_node.idx()].is_control()
-        )
-        .filter(
-        |loop_node|
-        {
-            editor.get_users(**loop_node).any(|user|!loop_nodes.contains(&user))
-        }
-    ).cloned().collect();
-
-    // If all loop variables are contained w/ PHIs already, no point in canonicalizing. 
-    if phis_to_add.iter().all(
-        |node| {
-            let Node::Phi { ref control, ref data } = editor.func().nodes[node.idx()] else {return false};
-            if *control == natural_loop.header {
-                true
-            } else {
-                false
-            }
-        }
-    ) {
-       return false;
-
-    }
-
-    if phis_to_add.is_empty() {
-        return false;
-    }
-
-    let loop_before_if_first = editor.get_users(natural_loop.header)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    let loop_before_if_last = editor.get_uses(loop_if).next().unwrap();
-        
-    let loop_exit_projection = editor.get_users(loop_if)
-        .filter(|id| !natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    let loop_continue_projection = editor.get_users(loop_if)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    // Control goes after the loop_continue_projection, and before whatever is after loop_continue_projection.
-    let loop_body_last = editor.get_uses(natural_loop.header)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    // ========= Do transformation ===========:
-
-    let num_loop_predecessors = editor.get_uses(natural_loop.header).count();
-
-    // Add PHIs
-    for data_in_loop in phis_to_add {
-        editor.edit(|mut edit| {
-            let ty = typing[data_in_loop.idx()];
-            let undef = Node::Undef { ty };
-            let undef = edit.add_node(undef);
-            let mut data = vec![undef; num_loop_predecessors];
-            data[header_continue_idx] = data_in_loop;
-            let new_phi = Node::Phi { control: natural_loop.header, data: data.into()};
-            let new_phi = edit.add_node(new_phi);
-            edit.replace_all_uses_where(data_in_loop, new_phi, |usee| !loop_nodes.contains(usee) && *usee != new_phi)
-        });
-    }
-
-    // Add PHI for loop condition
-    editor.edit(|mut edit| {
-        let bool_ty = typing[condition_node.idx()];
-        let true_const = Constant::Boolean(true);
-        let true_const = edit.add_constant(true_const); 
-        let true_const = Node::Constant { id: true_const };
-        let true_const = edit.add_node(true_const);
-        
-        let mut data = vec![true_const; num_loop_predecessors];
-        data[header_continue_idx] = condition_node;
-        let new_phi = Node::Phi { control: natural_loop.header, data: data.into()};
-        let new_phi = edit.add_node(new_phi);
-        edit.replace_all_uses_where(condition_node, new_phi, |usee| *usee == loop_if)
-    });
-
-    // Convert to while loop if not a while loop already.
-    if !editor.get_users(natural_loop.header).contains(&loop_if) {
-        editor.edit(|mut edit| {
-            // Have fun understanding this!
-            edit = edit.replace_all_uses(loop_continue_projection, loop_before_if_last)?;
-            edit = edit.replace_all_uses_where(natural_loop.header, loop_continue_projection, |usee| *usee == loop_before_if_first)?;
-            edit = edit.replace_all_uses_where(loop_before_if_last, natural_loop.header, |usee| *usee == loop_if)?;
-            
-            Ok(edit)
-        });
-
-        // for phi_to_add in while_loop_conversion {
-        //     editor.edit(|mut edit| {
-        //         let PhiToAdd { joining_phi, internal_phi, initializer } = phi_to_add;
-        //         let mut data = Box::new([NodeID::new(0); 2]);
-        //         data[header_initial_idx] = initializer;
-        //         data[header_continue_idx] = internal_phi;
-        //         let node = Node::Phi { control: natural_loop.header, data };
-        //         let new_phi = edit.add_node(node);
-        //         edit.replace_all_uses_where(internal_phi, new_phi, |usee| *usee == joining_phi)
-        //     });
-        //     println!("adding phi");
-        // }
-    
-    }
-
-    // Change loop bounds
-    // editor.edit(|edit| 
-    //     edit.replace_all_uses_where(iv_expression, base_iv.node, |usee| *usee == condition_node) 
-    // );
-
-    true
-
-
-}
-
-pub struct LoopGuard {
-    guard_if: NodeID,
-    loop_entered: NodeID,
-    loop_avoided: NodeID,
-}
-
-// Returns the 
-pub fn get_guard(
-    editor: &mut FunctionEditor,
-    natural_loop: &Loop,
-    if_node: NodeID,
-) -> Option<LoopGuard> {
-        // Given loop condition (iv_phi ? bound_expr)
-
-    // Q: What if iv_phi isn't a PHI, but instead a more complex expression.
-    // A: Idk!
-
-    // Q: What if idx_phi.init changes from when the loop is entered vs where the guard is?
-    // A: Guards have to be immediate, later we can look through control dominators blah blah.
-    
-    // Search for a condition (idx_phi.init ? bound_expr) immediately before the loop is entered 
-    // (header predecessor)
-    let Node::If { control: pred, cond: loop_condition } = 
-        editor.func().nodes[if_node.idx()] else {return None};
-
-    // Rely on GVN that the initializers will be the same exact node. 
-    let mut header_preds = editor.get_uses(natural_loop.header)
-        .filter(|pred| !natural_loop.control[pred.idx()]);
-
-    let Some(loop_pred) =  header_preds.next() else {return None};
-    if header_preds.next().is_some() {return None}; // If there is more than one header predecessor.
-
-    let Node::Projection { control: guard_if_node, ref selection } = 
-        editor.func().nodes[loop_pred.idx()] else {return None};
-
-    let Node::If { control: guard_if_pred, cond: guard_cond } = 
-        editor.func().nodes[guard_if_node.idx()] else {return None};
-
-    let loop_entered_proj = loop_pred;
-
-    // The if user that isn't the entered proj:
-    let Some(loop_avoided_proj) = editor.get_users(guard_if_node).filter(|n| *n != loop_entered_proj).next() else {return None};
-
-    let Node::Binary { left: guard_cond_left, right: guard_cond_right, op: guard_cond_op } = 
-        editor.func().nodes[guard_cond.idx()] else {return None};
-
-    // Check that the side of the exit condition is the same, or the initializer is the same.
-    let Node::Binary {left: latch_left, right: latch_right, op: latch_op } =
-        editor.func().nodes[loop_condition.idx()] else {return None};
-
-    // Check for Specific Pattern for do-while loops that have weird ivar + 1 < dc bound.
-    // This is the worst code I have ever written in my life.
-    let blah = {
-        if let Node::Binary { left: latch_add_left, right: latch_add_right, op: add_op } = &editor.func().nodes[latch_left.idx()] {
-            
-            // FIXME: Better utilities for comparing equiv of expressions. Blah.
-            let left_is_one = if let Node::Constant { id } = &editor.func().nodes[latch_add_left.idx()]  {
-                editor.get_constant(*id).is_one()
-            } else {
-                false
-            };
-
-            let right_is_one = if let Node::Constant { id } = &editor.func().nodes[latch_add_right.idx()]  {
-                editor.get_constant(*id).is_one()
-            } else {
-                false
-            };
-
-            if !(right_is_one || left_is_one) {
-                false
-            } else if !(*add_op == BinaryOperator::Add) {
-                false
-            } else {
-                let n = if (right_is_one) {
-                    &editor.func().nodes[latch_add_left.idx()]
-                } else {
-                    &editor.func().nodes[latch_add_right.idx()]
-                };
-
-                if let Node::Phi {control: phi_control, data} = n {
-                    if *phi_control == natural_loop.header {
-                        let Node::Region { preds } = &editor.func().nodes[natural_loop.header.idx()] else {panic!()};
-                        let init_idx = preds.iter().position(|node| *node == loop_pred ).unwrap();
-                        let init_value = data[init_idx];
-
-                        // Now, we have all the pieces, compare to the guard condition. 
-                        if latch_op == guard_cond_op && guard_cond_left == init_value && guard_cond_right == latch_right {
-                            return Some(LoopGuard { guard_if: guard_if_node, loop_entered: loop_entered_proj, loop_avoided: loop_avoided_proj });
-                        } else {
-                            return None;
-                        }
-                    } else {
-                        false
-                    }
-                } else {
-                    false
-                }
-            }
-
-        } else {
-            false
-        }
-    };
-
-    if blah {
-        return Some(LoopGuard { guard_if: guard_if_node, loop_entered: loop_entered_proj, loop_avoided: loop_avoided_proj });
-    }
-    
-
-    // Replace phis in the loop latch w/ their initializers.
-
-    // General Case:
-    let latch_left = if let Node::Phi { control: left_control, data } = &editor.func().nodes[latch_left.idx()] {
-        if *left_control == natural_loop.header {
-            let Node::Region { preds } = &editor.func().nodes[natural_loop.header.idx()] else {panic!()};
-            let init_idx = preds.iter().position(|node| *node == loop_pred ).unwrap();
-            
-            data[init_idx]
-        } else {
-            latch_left
-        }
-    } else {
-        latch_left
-    };
-
-    let latch_right = if let Node::Phi { control: right_control, data } = &editor.func().nodes[latch_right.idx()] {
-        if *right_control == natural_loop.header {
-            let Node::Region { preds } = &editor.func().nodes[natural_loop.header.idx()] else {panic!()};
-            let init_idx = preds.iter().position(|node| *node == loop_pred ).unwrap();
-            
-            data[init_idx]
-        } else {
-            latch_right
-        }
-    } else {
-        latch_right
-    };
-
-    // FIXME: More comprehensive condition equivalance. 
-    // Check condition equivalence:
-    if latch_op == guard_cond_op && guard_cond_left == latch_left && guard_cond_right == latch_right {
-        return Some(LoopGuard { guard_if: guard_if_node, loop_entered: loop_entered_proj, loop_avoided: loop_avoided_proj });
-    } else {
-        return None;
-    }
-}
-
-/** Attempts to converts a simple natural loop to a while loop
-  by moving all control between the loop header and the loop condition to after the loop true condition, 
-  but before the header.
- * */
-pub fn convert_to_while_loop(
-    editor: &mut FunctionEditor,
-    natural_loop: &Loop,
-    loop_exit: Option<LoopExit>,
-    add_guard_flag: bool,
-) -> bool {
-
-    // FIXME: Check that Loop is simple.  
-    let Some(LoopExit::Conditional { if_node, condition_node: _ }) = loop_exit.clone() else {return false};
-
-    // FIXME: Check whether the loop is guaranteed to be entered.
-    // i.e add a guard if needed. 
-    let guard = match get_guard(editor, natural_loop, if_node) {
-        Some(v) => v,
-        None => return false,
-    };
-
-    // Find the joining region for the guard and the loop exit.
-    // FIXME: For now, just assume its always the node following the guard loop_avoided projection. This is probably always the case. 
-    let LoopGuard { guard_if, loop_entered, loop_avoided } = guard;
-    let Some(joining_region) = editor.get_users(loop_avoided).next() else {return false;};
-
-    // For PHIs in the loop (but not of the loop header), that this joining region controls, need
-    // to add a version to the loop header, initialized to the same thing as the loop non-taken, and
-    // updated when the loop is taken to be the internal version. 
-    let loop_exit_proj = editor.get_users(if_node).filter(|node| !natural_loop.control[node.idx()]).next().unwrap();
-     
-    // Indicies for joining phis
-    let joining_loop_avoided_idx = editor.func().nodes[joining_region.idx()].try_region().unwrap().iter().position(|pred| *pred == loop_avoided).unwrap();
-    let joining_loop_exit_idx = editor.func().nodes[joining_region.idx()].try_region().unwrap().iter().position(|pred| *pred == loop_exit_proj).unwrap();
-
-    let header_initial_idx = editor.func().nodes[natural_loop.header.idx()].try_region().unwrap().iter().position(|pred| !natural_loop.control[pred.idx()]).unwrap();
-    let header_continue_idx = editor.func().nodes[natural_loop.header.idx()].try_region().unwrap().iter().position(|pred| natural_loop.control[pred.idx()]).unwrap();
-
-    let joining_phis = editor.get_users(joining_region).filter(|node| editor.func().nodes[node.idx()].is_phi());
-
-    // If the PHI in the joining region attempts to pull from a phi on the loop_exit_idx edge, which is internal to the loop 
-    // (in loop but not in loop header, add a phi to loop header)
-    struct PhiToAdd {
-        joining_phi: NodeID, // 
-        internal_phi: NodeID,
-        initializer: NodeID,
-    }
-
-    let phis_to_add: Vec<_> = joining_phis.filter_map(|phi| {
-        let Node::Phi { control, ref data } = &editor.func().nodes[phi.idx()] else {unreachable!()};
-
-        // control is joining_region. 
-
-        let loop_exit_node = data[joining_loop_exit_idx];
-
-        let Node::Phi {control: loop_phi_control, data: ref _loop_phi_data} = editor.func().nodes[loop_exit_node.idx()] else {return None};
-
-        if loop_phi_control == natural_loop.header {return None};
-
-        if !natural_loop.control[loop_phi_control.idx()] {
-            todo!("WHAT")
-        }
-
-        // Initializer is whatever the phi in the joining region takes if the loop is never run. 
-        let initializer = data[joining_loop_avoided_idx];
-
-        Some(PhiToAdd {joining_phi: phi, internal_phi: loop_exit_node, initializer: initializer })
-    }).collect();
-
-    // Get the control in between the header and before the condition,
-    
-    // If the header -> if, then there is no control before the condition, so it's a while loop.
-    if editor.get_uses(if_node).contains(&natural_loop.header) {
-        return false
-    }
-
-    let loop_before_if_first = editor.get_users(natural_loop.header)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    let loop_before_if_last = editor.get_uses(if_node).next().unwrap();
-        
-    // assert_ne!(loop_before_if_first, loop_before_if_last);
-    
-    let loop_exit_projection = editor.get_users(if_node)
-        .filter(|id| !natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    let loop_continue_projection = editor.get_users(if_node)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-    // Control goes after the loop_continue_projection, and before whatever is after loop_continue_projection.
-    let loop_body_last = editor.get_uses(natural_loop.header)
-        .filter(|id| natural_loop.control[id.idx()])
-        .next()
-        .unwrap();
-
-
-    for phi_to_add in phis_to_add {
-        editor.edit(|mut edit| {
-            let PhiToAdd { joining_phi, internal_phi, initializer } = phi_to_add;
-            let mut data = Box::new([NodeID::new(0); 2]);
-            data[header_initial_idx] = initializer;
-            data[header_continue_idx] = internal_phi;
-            let node = Node::Phi { control: natural_loop.header, data };
-            let new_phi = edit.add_node(node);
-            edit.replace_all_uses_where(internal_phi, new_phi, |usee| *usee == joining_phi)
-        });
-        println!("adding phi");
-    }
-
-    editor.edit(|mut edit| {
-        // Have fun understanding this!
-        edit = edit.replace_all_uses(loop_continue_projection, loop_before_if_last)?;
-        edit = edit.replace_all_uses_where(natural_loop.header, loop_continue_projection, |usee| *usee == loop_before_if_first)?;
-        edit = edit.replace_all_uses_where(loop_before_if_last, natural_loop.header, |usee| *usee == if_node)?;
-        
-        Ok(edit)
-    });
-    true
-}
-
-pub fn has_alternate_bounds(
-    function: &Function, 
-    l: &Loop, 
-    condition_node: NodeID, 
-    basic_ivs: &[BasicInductionVariable],
-    loop_variance: LoopVarianceInfo,
-) -> Option<(NodeID, BasicInductionVariable)> // iv_expression, base_iv
-{
-    // Analyze Loop Bound (pattern match w/ )
-    let alternate_iv = basic_ivs.iter().filter_map(|iv|
-        {
-            match &function.nodes[condition_node.idx()] {
-                Node::Start => todo!(),
-                Node::Phi { control, data } => todo!(),
-                Node::Reduce { control, init, reduct } => todo!(),
-                Node::Parameter { index } => todo!(),
-                Node::Constant { id } => todo!(),
-                Node::Unary { input, op } => todo!(),
-                Node::Ternary { first, second, third, op } => todo!(),
-                Node::Binary { left, right, op } => {
-                    match op {
-                        BinaryOperator::LT => {
-                            // Check for a loop guard condition.
-                            // ADDME: Check if the condition is *normal* already, and then check if the rest of the loop is normal.
-                            
-                            // left + 1 < right
-                            let Node::Binary { left: inner_left, right: inner_right, op: inner_op } = function.nodes[left.idx()] else {return None};
-                            if inner_op == BinaryOperator::Add &&
-                                ((inner_left == iv.update && inner_right == iv.node) || 
-                                (inner_right == iv.update && inner_left == iv.node)) &&
-                                loop_variance.map[right.idx()] == LoopVariance::Invariant 
-                            {
-                                return Some((left.clone(), iv.clone()));
-                            } else {
-                                return None;
-                            }
-    
-                        }
-                        BinaryOperator::LTE => todo!(), 
-                        BinaryOperator::GT => todo!(),
-                        BinaryOperator::GTE => todo!(),
-                        BinaryOperator::EQ => todo!(),
-                        BinaryOperator::NE => todo!(),
-                        _ => None,
-                    }
-                    
-                }
-                _ => None,
-            }
-        }
-    ).next();
-    alternate_iv
-}
-
-
-pub fn canonicalize_loop_old(
-    editor: &mut FunctionEditor,
-    loop_exit: Option<LoopExit>,
-    fork_join_map: &HashMap<NodeID, NodeID>,
-    l: &Loop,
-) -> bool {
-    
-    let Some(loop_condition) = loop_exit else {return false};
-
-    let LoopExit::Conditional { if_node: loop_if, condition_node } = loop_condition.clone() else {return false};
-
-    // FIXME: Need to be more careful abo  ut changing the conditions if we are a do-while loop,
-
-    // Changing loop conditions in canonicalization *actually* changes the number of times the loop runs.
-    // If there is no internal control, this doesn't matter. 
-    // If there is internal control, then changing loop iterations might mater.
-
-    // If the IF doesn't directly use the header, then there might be side-effects inside the loop,
-    // so we don't canonicalize
-    if !editor.get_uses(loop_if).contains(&l.header) {
-        return false
-    }
-
-    let function = editor.func();
-
-    // Compute loop variance
-    let loop_variance = compute_loop_variance(&editor, &l);
-
-    // Compute induction vars
-    let basic_ivs = compute_basic_induction_vars(function, &l, &loop_variance); 
-
-    // let Some((iv_expression, base_iv)) = None; //has_alternate_bounds(editor.func(), l, condition_node, &basic_ivs, loop_variance) else {return false};
-    // let iv_expression = iv_expression.clone();
-    // let base_iv = base_iv.clone();
-
-    // // If there are users of iv_expression (not just the loop bound condition), then abort
-    // if editor.get_users(iv_expression).count() > 2 {return false};
-
-    // // Replace external_uses uses of data with phi.
-    // // Panic on internal uses.
-    // struct PhiDataCycle  {
-    //     phi: NodeID, 
-    //     data: NodeID,
-    //     external_uses: Vec<NodeID>,
-    //     internal_uses: Vec<NodeID>
-    // }
-
-    // // The initiailzer position for all loop phis.
-    // let loop_phi_init_idx = editor.get_uses(l.header)
-    //     .position(|node| !l.control[node.idx()]  // Position of the predecessor (used by header but not in loop body.)
-    // ).unwrap();
-
-    // let data_use_locations = get_loop_data_location(editor, l);
-
-    // let mut changed = false;
-
-    // // Check all PHIs controlled by the loop
-    // let loop_phis: Option<Vec<PhiDataCycle>> = editor.get_users(l.header).filter(|n| editor.func().nodes[n.idx()].is_phi())
-    //     .filter(|phi| *phi != base_iv.node)
-    //     .map(|phi: NodeID| {
-        
-    //     // There should only be one candidate data,   
-    //     // but possibly multiple external uses. z
-
-    //     let initializer_node_id =  editor.func().nodes[phi.idx()].try_phi().unwrap().1[loop_phi_init_idx];
-
-    //     // Check if any use is in a cycle w/ the phi.
-    //     let mut data_cycles =
-    //         editor.get_uses(phi)
-    //             .filter(|phi_use| 
-    //                 *phi_use != initializer_node_id) // Not the initializer. 
-    //             .filter_map(|phi_use| {
-
-    //                 // If the data node is not in a cycle w/ the phi, 
-    //                 if !walk_all_uses(phi_use, editor).contains(&phi) {return None};
-
-    //                 // Find users of phi_use that are outside the loop, these we will change to use the phi.
-    //                 let (internal_uses, external_uses) = editor
-    //                     .get_users(phi_use)
-    //                     .filter_map(|data_user| {
-    //                         Some(data_user)        
-    //                     }).partition(|data_user| {
-    //                         match data_use_locations[data_user.idx()] {
-    //                             DataUseLoopLocation::Unknown => todo!(),
-    //                             DataUseLoopLocation::Inside => true,
-    //                             DataUseLoopLocation::Outside => false,
-    //                         }
-    //                     });
-
-    //                 Some((phi_use, internal_uses, external_uses))    
-    //             });
-            
-        
-    //     let Some((data, internal_uses, external_uses)) = data_cycles.next() else {
-    //         return None;
-    //     };
-
-    //     // There should only be one cycle
-    //     if data_cycles.next().is_some() {
-    //         return None;
-    //     }
-
-    //     Some(PhiDataCycle {
-    //         phi,
-    //         data,
-    //         external_uses,
-    //         internal_uses,
-    //     })
-    // }).collect();
-
-    // // If any PHIs are invalid, (not in cycles, )
-    // let Some(loop_phis) = loop_phis else {
-    //     return false;
-    // };
-
-    // // Make sure all phi data cycles are fully contained.
-    // let used_outside_loop = loop_phis.iter()
-    //     .any(|transform_info: &PhiDataCycle| 
-    // {   
-    //     let PhiDataCycle { phi, data, external_uses, internal_uses } = transform_info;
-
-    //     // Check usres of the PHI, make sure they aren't outside the loop 
-    //     // Unless they would be outside because of the use we are going to get rid of, 
-    //     // need a more complicated use location analysis for this. 
-    //     if editor.get_users(*phi)
-    //         .any(|node|
-    //             {
-    //                 if node == *data {
-    //                     return false;
-    //                 }
-
-    //                 let stop_on: HashSet<NodeID> = editor.node_ids().filter(|n| {
-    //                     if *n == *data {
-    //                         return true
-    //                     };
-
-    //                     let node_data = &editor.func().nodes[n.idx()];
-
-    //                     // Stop on Control. 
-    //                     if node_data.is_control() {
-    //                         return true;
-    //                     }
-    //                     // Stop on PHIs. 
-    //                     if node_data.is_phi() {
-    //                         // Need to maybe not stop on PHIs, but only stop on some of their incoming edges,
-    //                         // depending 
-    //                         let control = node_data.try_phi().unwrap().0;
-    //                         return l.control[control.idx()];
-    //                     }
-
-    //                     // Stop on Reduces.
-    //                     if node_data.is_reduce() {
-    //                         let control = node_data.try_reduce().unwrap().0;
-    //                         return l.control[control.idx()];
-    //                     }
-
-    //                     false
-    //                 }).collect();
-
-    //                 let outside_loop = editor.node_ids().filter(|n| editor.func().nodes[n.idx()].is_control() && !l.control[n.idx()]);
-
-    //                 // If any uses are control nodes *outside* the loop, 
-    //                 let node_uses = walk_all_users_stop_on(node, editor, stop_on);
-
-    //                 // TODO: Do intersection lazily? 
-    //                 let set1: HashSet<_> = HashSet::from_iter(outside_loop);
-    //                 let set2: HashSet<_> = HashSet::from_iter(node_uses);
-
-    //                 // If there is no intersection, then it is inside the loop
-    //                 if set1.intersection(&set2).next().is_none() {
-    //                     false // No intersection, so all users of this phi are good
-    //                 } else {
-    //                     true // Intersection, so some user of this phi leaves the loop, and we can't fix it by transforming.
-    //                 }                    
-    //             }
-    //     ) {
-    //         return true;
-    //     } else {
-    //         return false;
-    //     }        
-    // });
-
-    // if used_outside_loop {
-    //     return changed;
-    // }
-
-    // // Change loop bounds
-    // editor.edit(|edit| 
-    //     edit.replace_all_uses_where(iv_expression, base_iv.node, |usee| *usee == condition_node) 
-    // );
-
-    // changed = true;
-
-    // for transform_info in loop_phis {
-    //     editor.edit(|mut edit|
-    //         {
-    //             edit.replace_all_uses_where(transform_info.data, transform_info.phi, |usee| transform_info.external_uses.contains(usee))
-    //         }
-    //     );
-    // }
-    
-    // changed
-    false
-}
diff --git a/hercules_test/hercules_interpreter/src/lib.rs b/hercules_test/hercules_interpreter/src/lib.rs
index baf0093e..3f12618c 100644
--- a/hercules_test/hercules_interpreter/src/lib.rs
+++ b/hercules_test/hercules_interpreter/src/lib.rs
@@ -1,7 +1,5 @@
 pub mod interpreter;
 pub mod value;
-extern crate juno_scheduler;
-extern crate postcard;
 
 use std::fs::File;
 use std::io::Read;
diff --git a/hercules_test/hercules_tests/tests/fork_transform_tests.rs b/hercules_test/hercules_tests/tests/fork_transform_tests.rs
index 16813b03..432fdda0 100644
--- a/hercules_test/hercules_tests/tests/fork_transform_tests.rs
+++ b/hercules_test/hercules_tests/tests/fork_transform_tests.rs
@@ -4,7 +4,6 @@ use hercules_interpreter::*;
 use hercules_ir::ID;
 use juno_scheduler::ir::*;
 
-extern crate rand;
 use juno_scheduler::pass;
 use juno_scheduler::{default_schedule, run_schedule_on_hercules};
 use rand::Rng;
diff --git a/hercules_test/hercules_tests/tests/forkify_tests.rs b/hercules_test/hercules_tests/tests/forkify_tests.rs
index 025aaad3..5a8bff1a 100644
--- a/hercules_test/hercules_tests/tests/forkify_tests.rs
+++ b/hercules_test/hercules_tests/tests/forkify_tests.rs
@@ -7,7 +7,6 @@ use hercules_interpreter::*;
 use juno_scheduler::ir::*;
 use juno_scheduler::pass;
 
-extern crate rand;
 use juno_scheduler::{default_schedule, run_schedule_on_hercules};
 use rand::Rng;
 
diff --git a/hercules_test/hercules_tests/tests/interpreter_tests.rs b/hercules_test/hercules_tests/tests/interpreter_tests.rs
index 69e1920e..a779c70b 100644
--- a/hercules_test/hercules_tests/tests/interpreter_tests.rs
+++ b/hercules_test/hercules_tests/tests/interpreter_tests.rs
@@ -6,7 +6,6 @@ use hercules_ir::ID;
 use juno_scheduler::ir::*;
 use juno_scheduler::pass;
 
-extern crate rand;
 use juno_scheduler::{default_schedule, run_schedule_on_hercules};
 use rand::Rng;
 
diff --git a/hercules_test/hercules_tests/tests/loop_tests.rs b/hercules_test/hercules_tests/tests/loop_tests.rs
index 29b8692b..55da702d 100644
--- a/hercules_test/hercules_tests/tests/loop_tests.rs
+++ b/hercules_test/hercules_tests/tests/loop_tests.rs
@@ -5,7 +5,6 @@ use hercules_ir::ID;
 use juno_scheduler::ir::*;
 use juno_scheduler::pass;
 
-extern crate rand;
 use juno_scheduler::{default_schedule, run_schedule_on_hercules};
 use rand::random;
 use rand::Rng;
@@ -333,7 +332,6 @@ fn implicit_clone_pipeline() {
     println!("result: {:?}", result_1);
     let schedule = default_schedule![
         ////Xdot,,
-        LoopCanonicalization,
         Forkify,
         ForkGuardElim,
         Forkify,
diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs
index 14dd828b..11a8ec53 100644
--- a/juno_scheduler/src/compile.rs
+++ b/juno_scheduler/src/compile.rs
@@ -104,9 +104,6 @@ impl FromStr for Appliable {
             "forkify" => Ok(Appliable::Pass(ir::Pass::Forkify)),
             "gcm" | "bbs" => Ok(Appliable::Pass(ir::Pass::GCM)),
             "gvn" => Ok(Appliable::Pass(ir::Pass::GVN)),
-            "loop-canon" | "loop-canonicalization" => {
-                Ok(Appliable::Pass(ir::Pass::LoopCanonicalization))
-            }
             "infer-schedules" => Ok(Appliable::Pass(ir::Pass::InferSchedules)),
             "inline" => Ok(Appliable::Pass(ir::Pass::Inline)),
             "ip-sroa" | "interprocedural-sroa" => {
diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs
index aa9b2367..d6a41baf 100644
--- a/juno_scheduler/src/ir.rs
+++ b/juno_scheduler/src/ir.rs
@@ -8,7 +8,6 @@ pub enum Pass {
     DCE,
     DeleteUncalled,
     FloatCollections,
-    LoopCanonicalization,
     ForkGuardElim,
     ForkSplit,
     ForkCoalesce,
diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs
index 33a7b480..76e81ee9 100644
--- a/juno_scheduler/src/pm.rs
+++ b/juno_scheduler/src/pm.rs
@@ -1785,33 +1785,6 @@ fn run_pass(
             // Put BasicBlocks back, since it's needed for Codegen.
             pm.bbs = bbs;
         }
-        Pass::LoopCanonicalization => {
-            assert!(args.is_empty());
-            pm.make_fork_join_maps();
-            pm.make_control_subgraphs();
-            pm.make_loops();
-            pm.make_typing();
-            let fork_join_maps = pm.fork_join_maps.take().unwrap();
-            let loops = pm.loops.take().unwrap();
-            let control_subgraphs = pm.control_subgraphs.take().unwrap();
-            let typing = pm.typing.take().unwrap();
-            for ((((func, fork_join_map), loop_nest), control_subgraph), typing) in
-                build_selection(pm, selection)
-                    .into_iter()
-                    .zip(fork_join_maps.iter())
-                    .zip(loops.iter())
-                    .zip(control_subgraphs.iter())
-                    .zip(typing.iter())
-            {
-                let Some(mut func) = func else {
-                    continue;
-                };
-                // changed |= loop_canonicalization(&mut func, control_subgraph, fork_join_map, loop_nest, typing);
-                // func.modified();
-            }
-            pm.delete_gravestones();
-            pm.clear_analyses();
-        }
     }
     println!("Ran Pass: {:?}", pass);
 
-- 
GitLab