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