use std::cmp::{max, min}; use std::collections::HashSet; use std::iter::zip; use hercules_ir::dataflow::*; use hercules_ir::def_use::get_uses; use hercules_ir::ir::*; use crate::*; /* * The ccp lattice tracks, for each node, the following information: * 1. Reachability - is it possible for this node to be reached during any * execution? * 2. Constant - does this node evaluate to a constant expression? * The ccp lattice is formulated as a combination of consistuent lattices. The * flow function for the ccp dataflow analysis "crosses" information across the * sub lattices - for example, whether a condition is constant may inform * whether a branch target is reachable. This analysis uses interpreted * constants, so constant one plus constant one results in constant two. */ #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CCPLattice { reachability: ReachabilityLattice, constant: ConstantLattice, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ReachabilityLattice { Unreachable, Reachable, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ConstantLattice { Top, Constant(Constant), Bottom, } impl CCPLattice { fn is_reachable(&self) -> bool { self.reachability == ReachabilityLattice::Reachable } fn get_constant(&self) -> Option<Constant> { if let ConstantLattice::Constant(cons) = &self.constant { Some(cons.clone()) } else { None } } } impl ReachabilityLattice { // We define join for the reachability lattice as for data nodes we join the reachability of // its uses since it is unreachable if any of its uses are unreachable fn join(a: &Self, b: &Self) -> Self { match (a, b) { (ReachabilityLattice::Reachable, ReachabilityLattice::Reachable) => { ReachabilityLattice::Reachable } _ => ReachabilityLattice::Unreachable, } } } impl ConstantLattice { fn is_top(&self) -> bool { *self == ConstantLattice::Top } fn is_bottom(&self) -> bool { *self == ConstantLattice::Bottom } } impl Semilattice for CCPLattice { fn meet(a: &Self, b: &Self) -> Self { CCPLattice { reachability: ReachabilityLattice::meet(&a.reachability, &b.reachability), constant: ConstantLattice::meet(&a.constant, &b.constant), } } fn bottom() -> Self { CCPLattice { reachability: ReachabilityLattice::bottom(), constant: ConstantLattice::bottom(), } } fn top() -> Self { CCPLattice { reachability: ReachabilityLattice::top(), constant: ConstantLattice::top(), } } } impl Semilattice for ReachabilityLattice { fn meet(a: &Self, b: &Self) -> Self { match (a, b) { (ReachabilityLattice::Unreachable, ReachabilityLattice::Unreachable) => { ReachabilityLattice::Unreachable } _ => ReachabilityLattice::Reachable, } } fn bottom() -> Self { ReachabilityLattice::Reachable } fn top() -> Self { ReachabilityLattice::Unreachable } } impl Semilattice for ConstantLattice { fn meet(a: &Self, b: &Self) -> Self { match (a, b) { (ConstantLattice::Top, b) => b.clone(), (a, ConstantLattice::Top) => a.clone(), (ConstantLattice::Constant(cons1), ConstantLattice::Constant(cons2)) => { if cons1 == cons2 { ConstantLattice::Constant(cons1.clone()) } else { ConstantLattice::Bottom } } _ => ConstantLattice::Bottom, } } fn bottom() -> Self { ConstantLattice::Bottom } fn top() -> Self { ConstantLattice::Top } } /* Macros used for constant propagation with intrinsic functions */ macro_rules! unary_float_intrinsic { ($intrinsic : expr, $constants : expr, $func : ident) => { if let Constant::Float32(v) = $constants[0] { ConstantLattice::Constant(Constant::Float32(ordered_float::OrderedFloat(v.$func()))) } else if let Constant::Float64(v) = $constants[0] { ConstantLattice::Constant(Constant::Float64(ordered_float::OrderedFloat(v.$func()))) } else { panic!("Unsupported combination of intrinsic {} and constant value. Did typechecking succeed?", $intrinsic.lower_case_name()) } } } macro_rules! binary_float_intrinsic { ($intrinsic : expr, $constants : expr, $func : ident) => { if let (Constant::Float32(x), Constant::Float32(y)) = ($constants[0], $constants[1]) { ConstantLattice::Constant(Constant::Float32(ordered_float::OrderedFloat(x.$func(**y)))) } else if let (Constant::Float64(x), Constant::Float64(y)) = ($constants[0], $constants[1]) { ConstantLattice::Constant(Constant::Float64(ordered_float::OrderedFloat(x.$func(**y)))) } else { panic!("Unsupported combination of intrinsic {} and constant values. Did typechecking succeed?", $intrinsic.lower_case_name()) } } } /* * Top level function to run conditional constant propagation. */ pub fn ccp(editor: &mut FunctionEditor, reverse_postorder: &Vec<NodeID>) { // Step 1: run ccp analysis to understand the function. let result = dataflow_global(editor.func(), reverse_postorder, |inputs, node_id| { ccp_flow_function(inputs, node_id, editor) }); // Check the results of CCP. In particular we assert that for every reachable node (except for // region and phi nodes) all of its uses are also reachable. For phi nodes we check this // property only on live branches and for regions we check that at least one of its // predecessors is reachable if it is reachable for node_idx in 0..result.len() { if !result[node_idx].is_reachable() { continue; } match &editor.func().nodes[node_idx] { Node::Region { preds } => { assert!(preds.iter().any(|n| result[n.idx()].is_reachable())) } Node::Phi { control, data } => { assert!(result[control.idx()].is_reachable()); let region_preds = if let Node::Region { preds } = &editor.func().nodes[control.idx()] { preds } else { panic!("A phi's control input must be a region node.") }; assert!(zip(region_preds.iter(), data.iter()).all(|(region, data)| { !result[region.idx()].is_reachable() || result[data.idx()].is_reachable() })); } _ => { assert!(get_uses(&editor.func().nodes[node_idx]) .as_ref() .iter() .all(|n| result[n.idx()].is_reachable())); } } } // Step 2: propagate constants. For each node that was found to have a constant value, we // create a node for that constant value, replace uses of the original node with the constant, // and finally delete the original node let mut unreachable: HashSet<NodeID> = HashSet::new(); for (idx, res) in result.into_iter().enumerate() { let old_id = NodeID::new(idx); if let Some(cons) = res.get_constant() { assert!(!editor.func().nodes[idx].is_control()); editor.edit(|mut edit| { // Get the ConstantID of this constant value. let cons_id = edit.add_constant(cons); // Add a constant IR node for this constant let cons_node = edit.add_node(Node::Constant { id: cons_id }); // Replace the original node with the constant node edit.sub_edit(old_id, cons_node); edit = edit.replace_all_uses(old_id, cons_node)?; edit.delete_node(old_id) }); } if !res.is_reachable() { unreachable.insert(old_id); } } // Step 3: delete dead branches. Any nodes that are unreachable should be // deleted. Any if or match nodes that now are light on users need to be // removed immediately, since if and match nodes have requirements on the // number of users. // Step 3.1: remove uses of data nodes in phi nodes corresponding to // unreachable uses in corresponding region nodes. for phi_id in (0..editor.func().nodes.len()).map(NodeID::new) { if unreachable.contains(&phi_id) { continue; } if let Node::Phi { control, data } = &editor.func().nodes[phi_id.idx()] { if let Node::Region { preds } = &editor.func().nodes[control.idx()] { let control = *control; let new_data = zip(preds.iter(), data.iter()) .filter(|(pred, _)| !unreachable.contains(pred)) .map(|(_, datum)| *datum) .collect(); editor.edit(|mut edit| { let new_node = edit.add_node(Node::Phi { control, data: new_data, }); edit.sub_edit(phi_id, new_node); edit = edit.replace_all_uses(phi_id, new_node)?; edit.delete_node(phi_id) }); } } } // Step 3.2: remove uses of unreachable nodes in region nodes. for region_id in (0..editor.func().nodes.len()).map(NodeID::new) { if unreachable.contains(®ion_id) { continue; } if let Node::Region { preds } = &editor.func().nodes[region_id.idx()] { let new_preds = preds .iter() .filter(|pred| !unreachable.contains(pred)) .map(|x| *x) .collect(); editor.edit(|mut edit| { let new_node = edit.add_node(Node::Region { preds: new_preds }); edit.sub_edit(region_id, new_node); edit = edit.replace_all_uses(region_id, new_node)?; edit.delete_node(region_id) }); } } // Step 3.3: remove if and match nodes with one reachable user. for branch_id in (0..editor.func().nodes.len()).map(NodeID::new) { if unreachable.contains(&branch_id) { continue; } if let Node::If { control, cond: _ } | Node::Match { control, sum: _ } = &editor.func().nodes[branch_id.idx()] { let control = *control; let users = editor.get_users(branch_id).collect::<Vec<NodeID>>(); let reachable_users = users .iter() .map(|x| *x) .filter(|user| !unreachable.contains(user)) .collect::<Vec<NodeID>>(); let the_reachable_user = reachable_users[0]; // The reachable users iterator will contain one user if we need to // remove this branch node. if reachable_users.len() == 1 { // The user is a projection node, which in turn has one user. editor.edit(|mut edit| { // Replace all uses of the single reachable user with the node preceeding the // branch node edit = edit.replace_all_uses(the_reachable_user, control)?; // Mark the reachable user and the branch as unreachable so they'll be deleted unreachable.insert(the_reachable_user); unreachable.insert(branch_id); Ok(edit) }); } } } // Step 3.4: delete unreachable nodes. editor.edit(|mut edit| { for node in unreachable { edit = edit.delete_node(node)?; } Ok(edit) }); } fn ccp_flow_function( inputs: &[CCPLattice], node_id: NodeID, editor: &FunctionEditor, ) -> CCPLattice { let node = &editor.func().nodes[node_id.idx()]; match node { Node::Start => CCPLattice::bottom(), Node::Region { preds } => preds.iter().fold(CCPLattice::top(), |val, id| { CCPLattice::meet(&val, &inputs[id.idx()]) }), // If node has only one output, if doesn't directly handle crossover of // reachability and constant propagation. Projection handles that. Node::If { control, cond: _ } => inputs[control.idx()].clone(), Node::Match { control, sum: _ } => inputs[control.idx()].clone(), Node::Fork { control, factors: _, } => inputs[control.idx()].clone(), Node::Join { control } => inputs[control.idx()].clone(), // Phi nodes must look at the reachability of the inputs to its // corresponding region node to determine the constant value being // output. Node::Phi { control, data } => { // Get the control predecessors of the corresponding region. let region_preds = if let Node::Region { preds } = &editor.func().nodes[control.idx()] { preds } else { panic!("A phi's control input must be a region node.") }; zip(region_preds.iter(), data.iter()).fold( CCPLattice { reachability: inputs[control.idx()].reachability.clone(), constant: ConstantLattice::top(), }, |val, (control_id, data_id)| { // If a control input to the region node is reachable, then // and only then do we meet with the data input's constant // lattice value. if inputs[control_id.idx()].is_reachable() { CCPLattice::meet(&val, &inputs[data_id.idx()]) } else { val } }, ) } // TODO: This should produce a constant zero if the dynamic constant for // for the corresponding fork is one. Node::ThreadID { control, dimension: _, } => inputs[control.idx()].clone(), Node::Reduce { control, init, reduct, } => { let reachability = inputs[control.idx()].reachability.clone(); if reachability == ReachabilityLattice::Reachable { let mut constant = inputs[init.idx()].constant.clone(); if inputs[reduct.idx()].is_reachable() { constant = ConstantLattice::meet(&constant, &inputs[reduct.idx()].constant); } CCPLattice { reachability, constant, } } else { CCPLattice { reachability, constant: ConstantLattice::top(), } } } Node::Return { control, data: _ } => inputs[control.idx()].clone(), Node::Parameter { index: _ } => CCPLattice::bottom(), // A constant node is the "source" of concrete constant lattice values. Node::Constant { id } => CCPLattice { reachability: ReachabilityLattice::bottom(), constant: ConstantLattice::Constant(editor.get_constant(*id).clone()), }, Node::DynamicConstant { id } => match *editor.get_dynamic_constant(*id) { DynamicConstant::Constant(value) => CCPLattice { reachability: ReachabilityLattice::bottom(), constant: ConstantLattice::Constant(Constant::UnsignedInteger64(value as u64)), }, _ => CCPLattice::bottom(), }, // Interpret unary op on constant. Node::Unary { input, op } => { let CCPLattice { ref reachability, ref constant, } = inputs[input.idx()]; let new_constant = if let ConstantLattice::Constant(cons) = constant { match (op, cons) { (UnaryOperator::Not, Constant::Boolean(val)) => Some(ConstantLattice::Constant(Constant::Boolean(!val))), (UnaryOperator::Not, Constant::Integer8(val)) => Some(ConstantLattice::Constant(Constant::Integer8(!val))), (UnaryOperator::Not, Constant::Integer16(val)) => Some(ConstantLattice::Constant(Constant::Integer16(!val))), (UnaryOperator::Not, Constant::Integer32(val)) => Some(ConstantLattice::Constant(Constant::Integer32(!val))), (UnaryOperator::Not, Constant::Integer64(val)) => Some(ConstantLattice::Constant(Constant::Integer64(!val))), (UnaryOperator::Not, Constant::UnsignedInteger8(val)) => Some(ConstantLattice::Constant(Constant::UnsignedInteger8(!val))), (UnaryOperator::Not, Constant::UnsignedInteger16(val)) => Some(ConstantLattice::Constant(Constant::UnsignedInteger16(!val))), (UnaryOperator::Not, Constant::UnsignedInteger32(val)) => Some(ConstantLattice::Constant(Constant::UnsignedInteger32(!val))), (UnaryOperator::Not, Constant::UnsignedInteger64(val)) => Some(ConstantLattice::Constant(Constant::UnsignedInteger64(!val))), (UnaryOperator::Neg, Constant::Integer8(val)) => val.checked_neg().map(|v| ConstantLattice::Constant(Constant::Integer8(v))), (UnaryOperator::Neg, Constant::Integer16(val)) => val.checked_neg().map(|v| ConstantLattice::Constant(Constant::Integer16(v))), (UnaryOperator::Neg, Constant::Integer32(val)) => val.checked_neg().map(|v| ConstantLattice::Constant(Constant::Integer32(v))), (UnaryOperator::Neg, Constant::Integer64(val)) => val.checked_neg().map(|v| ConstantLattice::Constant(Constant::Integer64(v))), (UnaryOperator::Neg, Constant::Float32(val)) => Some(ConstantLattice::Constant(Constant::Float32(-val))), (UnaryOperator::Neg, Constant::Float64(val)) => Some(ConstantLattice::Constant(Constant::Float64(-val))), (UnaryOperator::Cast(_), _) => Some(ConstantLattice::Bottom), _ => panic!("Unsupported combination of unary operation and constant value. Did typechecking succeed?") }.unwrap_or(ConstantLattice::bottom()) } else { constant.clone() }; CCPLattice { reachability: reachability.clone(), constant: new_constant, } } // Interpret binary op on constants. Node::Binary { left, right, op } => { let CCPLattice { reachability: ref left_reachability, constant: ref left_constant, } = inputs[left.idx()]; let CCPLattice { reachability: ref right_reachability, constant: ref right_constant, } = inputs[right.idx()]; let new_constant = if let ( ConstantLattice::Constant(left_cons), ConstantLattice::Constant(right_cons), ) = (left_constant, right_constant) { let new_cons = match (op, left_cons, right_cons) { (BinaryOperator::Add, Constant::Integer8(left_val), Constant::Integer8(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::Integer8(v)), (BinaryOperator::Add, Constant::Integer16(left_val), Constant::Integer16(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::Integer16(v)), (BinaryOperator::Add, Constant::Integer32(left_val), Constant::Integer32(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::Integer32(v)), (BinaryOperator::Add, Constant::Integer64(left_val), Constant::Integer64(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::Integer64(v)), (BinaryOperator::Add, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::UnsignedInteger8(v)), (BinaryOperator::Add, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::UnsignedInteger16(v)), (BinaryOperator::Add, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::UnsignedInteger32(v)), (BinaryOperator::Add, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => left_val.checked_add(*right_val).map(|v| Constant::UnsignedInteger64(v)), (BinaryOperator::Add, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Float32(*left_val + *right_val)), (BinaryOperator::Add, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Float64(*left_val + *right_val)), (BinaryOperator::Sub, Constant::Integer8(left_val), Constant::Integer8(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::Integer8(v)), (BinaryOperator::Sub, Constant::Integer16(left_val), Constant::Integer16(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::Integer16(v)), (BinaryOperator::Sub, Constant::Integer32(left_val), Constant::Integer32(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::Integer32(v)), (BinaryOperator::Sub, Constant::Integer64(left_val), Constant::Integer64(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::Integer64(v)), (BinaryOperator::Sub, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::UnsignedInteger8(v)), (BinaryOperator::Sub, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::UnsignedInteger16(v)), (BinaryOperator::Sub, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::UnsignedInteger32(v)), (BinaryOperator::Sub, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => left_val.checked_sub(*right_val).map(|v| Constant::UnsignedInteger64(v)), (BinaryOperator::Sub, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Float32(*left_val - *right_val)), (BinaryOperator::Sub, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Float64(*left_val - *right_val)), (BinaryOperator::Mul, Constant::Integer8(left_val), Constant::Integer8(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::Integer8(v)), (BinaryOperator::Mul, Constant::Integer16(left_val), Constant::Integer16(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::Integer16(v)), (BinaryOperator::Mul, Constant::Integer32(left_val), Constant::Integer32(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::Integer32(v)), (BinaryOperator::Mul, Constant::Integer64(left_val), Constant::Integer64(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::Integer64(v)), (BinaryOperator::Mul, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::UnsignedInteger8(v)), (BinaryOperator::Mul, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::UnsignedInteger16(v)), (BinaryOperator::Mul, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::UnsignedInteger32(v)), (BinaryOperator::Mul, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => left_val.checked_mul(*right_val).map(|v| Constant::UnsignedInteger64(v)), (BinaryOperator::Mul, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Float32(*left_val * *right_val)), (BinaryOperator::Mul, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Float64(*left_val * *right_val)), (BinaryOperator::Div, Constant::Integer8(left_val), Constant::Integer8(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::Integer8(v)), (BinaryOperator::Div, Constant::Integer16(left_val), Constant::Integer16(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::Integer16(v)), (BinaryOperator::Div, Constant::Integer32(left_val), Constant::Integer32(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::Integer32(v)), (BinaryOperator::Div, Constant::Integer64(left_val), Constant::Integer64(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::Integer64(v)), (BinaryOperator::Div, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::UnsignedInteger8(v)), (BinaryOperator::Div, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::UnsignedInteger16(v)), (BinaryOperator::Div, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::UnsignedInteger32(v)), (BinaryOperator::Div, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => left_val.checked_div(*right_val).map(|v| Constant::UnsignedInteger64(v)), (BinaryOperator::Div, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Float32(*left_val / *right_val)), (BinaryOperator::Div, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Float64(*left_val / *right_val)), (BinaryOperator::Rem, Constant::Integer8(left_val), Constant::Integer8(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::Integer8(v)), (BinaryOperator::Rem, Constant::Integer16(left_val), Constant::Integer16(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::Integer16(v)), (BinaryOperator::Rem, Constant::Integer32(left_val), Constant::Integer32(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::Integer32(v)), (BinaryOperator::Rem, Constant::Integer64(left_val), Constant::Integer64(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::Integer64(v)), (BinaryOperator::Rem, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::UnsignedInteger8(v)), (BinaryOperator::Rem, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::UnsignedInteger16(v)), (BinaryOperator::Rem, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::UnsignedInteger32(v)), (BinaryOperator::Rem, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => left_val.checked_rem(*right_val).map(|v| Constant::UnsignedInteger64(v)), (BinaryOperator::Rem, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Float32(*left_val % *right_val)), (BinaryOperator::Rem, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Float64(*left_val % *right_val)), (BinaryOperator::LT, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::Boolean(left_val < right_val)), (BinaryOperator::LT, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Boolean(*left_val < *right_val)), (BinaryOperator::LT, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Boolean(*left_val < *right_val)), (BinaryOperator::LTE, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::Boolean(left_val <= right_val)), (BinaryOperator::LTE, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Boolean(*left_val <= *right_val)), (BinaryOperator::LTE, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Boolean(*left_val <= *right_val)), (BinaryOperator::GT, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::Boolean(left_val > right_val)), (BinaryOperator::GT, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Boolean(*left_val > *right_val)), (BinaryOperator::GT, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Boolean(*left_val > *right_val)), (BinaryOperator::GTE, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::Boolean(left_val >= right_val)), (BinaryOperator::GTE, Constant::Float32(left_val), Constant::Float32(right_val)) => Some(Constant::Boolean(*left_val >= *right_val)), (BinaryOperator::GTE, Constant::Float64(left_val), Constant::Float64(right_val)) => Some(Constant::Boolean(*left_val >= *right_val)), // EQ and NE can be implemented more easily, since we don't // need to unpack the constants. (BinaryOperator::EQ, left_val, right_val) => Some(Constant::Boolean(left_val == right_val)), (BinaryOperator::NE, left_val, right_val) => Some(Constant::Boolean(left_val != right_val)), (BinaryOperator::Or, Constant::Boolean(left_val), Constant::Boolean(right_val)) => Some(Constant::Boolean(*left_val || *right_val)), (BinaryOperator::Or, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Integer8(left_val | right_val)), (BinaryOperator::Or, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Integer16(left_val | right_val)), (BinaryOperator::Or, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Integer32(left_val | right_val)), (BinaryOperator::Or, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Integer64(left_val | right_val)), (BinaryOperator::Or, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::UnsignedInteger8(left_val | right_val)), (BinaryOperator::Or, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::UnsignedInteger16(left_val | right_val)), (BinaryOperator::Or, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::UnsignedInteger32(left_val | right_val)), (BinaryOperator::Or, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::UnsignedInteger64(left_val | right_val)), (BinaryOperator::And, Constant::Boolean(left_val), Constant::Boolean(right_val)) => Some(Constant::Boolean(*left_val && *right_val)), (BinaryOperator::And, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Integer8(left_val & right_val)), (BinaryOperator::And, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Integer16(left_val & right_val)), (BinaryOperator::And, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Integer32(left_val & right_val)), (BinaryOperator::And, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Integer64(left_val & right_val)), (BinaryOperator::And, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::UnsignedInteger8(left_val & right_val)), (BinaryOperator::And, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::UnsignedInteger16(left_val & right_val)), (BinaryOperator::And, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::UnsignedInteger32(left_val & right_val)), (BinaryOperator::And, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::UnsignedInteger64(left_val & right_val)), (BinaryOperator::Xor, Constant::Boolean(left_val), Constant::Boolean(right_val)) => Some(Constant::Boolean(left_val ^ right_val)), (BinaryOperator::Xor, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Integer8(left_val ^ right_val)), (BinaryOperator::Xor, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Integer16(left_val ^ right_val)), (BinaryOperator::Xor, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Integer32(left_val ^ right_val)), (BinaryOperator::Xor, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Integer64(left_val ^ right_val)), (BinaryOperator::Xor, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::UnsignedInteger8(left_val ^ right_val)), (BinaryOperator::Xor, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::UnsignedInteger16(left_val ^ right_val)), (BinaryOperator::Xor, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::UnsignedInteger32(left_val ^ right_val)), (BinaryOperator::Xor, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::UnsignedInteger64(left_val ^ right_val)), (BinaryOperator::LSh, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Integer8(left_val << right_val)), (BinaryOperator::LSh, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Integer16(left_val << right_val)), (BinaryOperator::LSh, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Integer32(left_val << right_val)), (BinaryOperator::LSh, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Integer64(left_val << right_val)), (BinaryOperator::LSh, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::UnsignedInteger8(left_val << right_val)), (BinaryOperator::LSh, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::UnsignedInteger16(left_val << right_val)), (BinaryOperator::LSh, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::UnsignedInteger32(left_val << right_val)), (BinaryOperator::LSh, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::UnsignedInteger64(left_val << right_val)), (BinaryOperator::RSh, Constant::Integer8(left_val), Constant::Integer8(right_val)) => Some(Constant::Integer8(left_val >> right_val)), (BinaryOperator::RSh, Constant::Integer16(left_val), Constant::Integer16(right_val)) => Some(Constant::Integer16(left_val >> right_val)), (BinaryOperator::RSh, Constant::Integer32(left_val), Constant::Integer32(right_val)) => Some(Constant::Integer32(left_val >> right_val)), (BinaryOperator::RSh, Constant::Integer64(left_val), Constant::Integer64(right_val)) => Some(Constant::Integer64(left_val >> right_val)), (BinaryOperator::RSh, Constant::UnsignedInteger8(left_val), Constant::UnsignedInteger8(right_val)) => Some(Constant::UnsignedInteger8(left_val >> right_val)), (BinaryOperator::RSh, Constant::UnsignedInteger16(left_val), Constant::UnsignedInteger16(right_val)) => Some(Constant::UnsignedInteger16(left_val >> right_val)), (BinaryOperator::RSh, Constant::UnsignedInteger32(left_val), Constant::UnsignedInteger32(right_val)) => Some(Constant::UnsignedInteger32(left_val >> right_val)), (BinaryOperator::RSh, Constant::UnsignedInteger64(left_val), Constant::UnsignedInteger64(right_val)) => Some(Constant::UnsignedInteger64(left_val >> right_val)), _ => panic!("Unsupported combination of binary operation and constant values. Did typechecking succeed?") }; new_cons .map(|c| ConstantLattice::Constant(c)) .unwrap_or(ConstantLattice::bottom()) } else if (left_constant.is_top() && !right_constant.is_bottom()) || (!left_constant.is_bottom() && right_constant.is_top()) { ConstantLattice::top() } else { ConstantLattice::meet(left_constant, right_constant) }; CCPLattice { reachability: ReachabilityLattice::join(left_reachability, right_reachability), constant: new_constant, } } Node::Ternary { first, second, third, op, } => { let CCPLattice { reachability: ref first_reachability, constant: ref first_constant, } = inputs[first.idx()]; let CCPLattice { reachability: ref second_reachability, constant: ref second_constant, } = inputs[second.idx()]; let CCPLattice { reachability: ref third_reachability, constant: ref third_constant, } = inputs[third.idx()]; let new_constant = if let ( ConstantLattice::Constant(first_cons), ConstantLattice::Constant(second_cons), ConstantLattice::Constant(third_cons), ) = (first_constant, second_constant, third_constant) { let new_cons = match(op, first_cons, second_cons, third_cons) { (TernaryOperator::Select, Constant::Boolean(first_val), second_val, third_val) => if *first_val {second_val.clone()} else {third_val.clone()}, _ => panic!("Unsupported combination of ternary operation and constant values. Did typechecking succeed?") }; ConstantLattice::Constant(new_cons) } else if (first_constant.is_top() && !second_constant.is_bottom() && !third_constant.is_bottom()) || (!first_constant.is_bottom() && second_constant.is_top() && !first_constant.is_bottom()) || (!first_constant.is_bottom() && !second_constant.is_bottom() && third_constant.is_top()) { ConstantLattice::top() } else { ConstantLattice::meet(second_constant, third_constant) }; CCPLattice { reachability: ReachabilityLattice::join( first_reachability, &ReachabilityLattice::join(second_reachability, third_reachability), ), constant: new_constant, } } // Call nodes are uninterpretable. Node::Call { control: _, function: _, dynamic_constants: _, args, } => CCPLattice { reachability: args.iter().fold(ReachabilityLattice::bottom(), |val, id| { ReachabilityLattice::join(&val, &inputs[id.idx()].reachability) }), constant: ConstantLattice::bottom(), }, // Data projections are uninterpretable. Node::DataProjection { data, selection: _ } => CCPLattice { reachability: inputs[data.idx()].reachability.clone(), constant: ConstantLattice::bottom(), }, Node::IntrinsicCall { intrinsic, args } => { let mut new_reachability = ReachabilityLattice::bottom(); let mut new_constant = ConstantLattice::top(); let mut constants = vec![]; let mut all_constants = true; for arg in args.iter() { let CCPLattice { ref reachability, ref constant, } = inputs[arg.idx()]; new_reachability = ReachabilityLattice::join(&new_reachability, reachability); new_constant = ConstantLattice::meet(&new_constant, constant); if let ConstantLattice::Constant(constant) = constant { constants.push(constant); } else { all_constants = false; } } if all_constants { new_constant = match intrinsic { Intrinsic::Abs => { if let Constant::Integer8(i) = constants[0] { ConstantLattice::Constant(Constant::Integer8(i.abs())) } else if let Constant::Integer16(i) = constants[0] { ConstantLattice::Constant(Constant::Integer16(i.abs())) } else if let Constant::Integer32(i) = constants[0] { ConstantLattice::Constant(Constant::Integer32(i.abs())) } else if let Constant::Integer64(i) = constants[0] { ConstantLattice::Constant(Constant::Integer64(i.abs())) } else if let Constant::UnsignedInteger8(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger8(*i)) } else if let Constant::UnsignedInteger16(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger16(*i)) } else if let Constant::UnsignedInteger32(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger32(*i)) } else if let Constant::UnsignedInteger64(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger64(*i)) } else if let Constant::Float32(i) = constants[0] { ConstantLattice::Constant(Constant::Float32( ordered_float::OrderedFloat(i.abs()), )) } else if let Constant::Float64(i) = constants[0] { ConstantLattice::Constant(Constant::Float64( ordered_float::OrderedFloat(i.abs()), )) } else { panic!("Unsupported combination of intrinsic abs and constant value. Did typechecking succeed?") } } Intrinsic::ACos => unary_float_intrinsic!(intrinsic, constants, acos), Intrinsic::ACosh => unary_float_intrinsic!(intrinsic, constants, acosh), Intrinsic::ASin => unary_float_intrinsic!(intrinsic, constants, asin), Intrinsic::ASinh => unary_float_intrinsic!(intrinsic, constants, asinh), Intrinsic::ATan => unary_float_intrinsic!(intrinsic, constants, atan), Intrinsic::ATan2 => binary_float_intrinsic!(intrinsic, constants, atan2), Intrinsic::ATanh => unary_float_intrinsic!(intrinsic, constants, atanh), Intrinsic::Cbrt => unary_float_intrinsic!(intrinsic, constants, cbrt), Intrinsic::Ceil => unary_float_intrinsic!(intrinsic, constants, ceil), Intrinsic::Cos => unary_float_intrinsic!(intrinsic, constants, cos), Intrinsic::Cosh => unary_float_intrinsic!(intrinsic, constants, cosh), Intrinsic::Exp => unary_float_intrinsic!(intrinsic, constants, exp), Intrinsic::Exp2 => unary_float_intrinsic!(intrinsic, constants, exp2), Intrinsic::ExpM1 => unary_float_intrinsic!(intrinsic, constants, exp_m1), Intrinsic::Floor => unary_float_intrinsic!(intrinsic, constants, floor), Intrinsic::Ln => unary_float_intrinsic!(intrinsic, constants, ln), Intrinsic::Ln1P => unary_float_intrinsic!(intrinsic, constants, ln_1p), Intrinsic::Log => binary_float_intrinsic!(intrinsic, constants, log), Intrinsic::Log10 => unary_float_intrinsic!(intrinsic, constants, log10), Intrinsic::Log2 => unary_float_intrinsic!(intrinsic, constants, log2), Intrinsic::Max => { if let (Constant::Integer8(i), Constant::Integer8(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer8(max(*i, *j))) } else if let (Constant::Integer16(i), Constant::Integer16(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer16(max(*i, *j))) } else if let (Constant::Integer32(i), Constant::Integer32(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer32(max(*i, *j))) } else if let (Constant::Integer64(i), Constant::Integer64(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer64(max(*i, *j))) } else if let ( Constant::UnsignedInteger8(i), Constant::UnsignedInteger8(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger8(max(*i, *j))) } else if let ( Constant::UnsignedInteger16(i), Constant::UnsignedInteger16(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger16(max(*i, *j))) } else if let ( Constant::UnsignedInteger32(i), Constant::UnsignedInteger32(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger32(max(*i, *j))) } else if let ( Constant::UnsignedInteger64(i), Constant::UnsignedInteger64(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger64(max(*i, *j))) } else if let (Constant::Float32(i), Constant::Float32(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Float32(*i.max(j))) } else if let (Constant::Float64(i), Constant::Float64(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Float64(*i.max(j))) } else { panic!("Unsupported combination of intrinsic abs and constant value. Did typechecking succeed?") } } Intrinsic::Min => { if let (Constant::Integer8(i), Constant::Integer8(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer8(min(*i, *j))) } else if let (Constant::Integer16(i), Constant::Integer16(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer16(min(*i, *j))) } else if let (Constant::Integer32(i), Constant::Integer32(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer32(min(*i, *j))) } else if let (Constant::Integer64(i), Constant::Integer64(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Integer64(min(*i, *j))) } else if let ( Constant::UnsignedInteger8(i), Constant::UnsignedInteger8(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger8(min(*i, *j))) } else if let ( Constant::UnsignedInteger16(i), Constant::UnsignedInteger16(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger16(min(*i, *j))) } else if let ( Constant::UnsignedInteger32(i), Constant::UnsignedInteger32(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger32(min(*i, *j))) } else if let ( Constant::UnsignedInteger64(i), Constant::UnsignedInteger64(j), ) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::UnsignedInteger64(min(*i, *j))) } else if let (Constant::Float32(i), Constant::Float32(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Float32(*i.min(j))) } else if let (Constant::Float64(i), Constant::Float64(j)) = (constants[0], constants[1]) { ConstantLattice::Constant(Constant::Float64(*i.min(j))) } else { panic!("Unsupported combination of intrinsic abs and constant value. Did typechecking succeed?") } } Intrinsic::Pow => { if let Constant::UnsignedInteger32(p) = constants[1] { if let Constant::Integer8(i) = constants[0] { ConstantLattice::Constant(Constant::Integer8(i.pow(*p))) } else if let Constant::Integer16(i) = constants[0] { ConstantLattice::Constant(Constant::Integer16(i.pow(*p))) } else if let Constant::Integer32(i) = constants[0] { ConstantLattice::Constant(Constant::Integer32(i.pow(*p))) } else if let Constant::Integer64(i) = constants[0] { ConstantLattice::Constant(Constant::Integer64(i.pow(*p))) } else if let Constant::UnsignedInteger8(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger8(i.pow(*p))) } else if let Constant::UnsignedInteger16(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger16(i.pow(*p))) } else if let Constant::UnsignedInteger32(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger32(i.pow(*p))) } else if let Constant::UnsignedInteger64(i) = constants[0] { ConstantLattice::Constant(Constant::UnsignedInteger64(i.pow(*p))) } else { panic!("Unsupported combination of intrinsic pow and constant values. Did typechecking succeed?") } } else { panic!("Unsupported combination of intrinsic pow and constant values. Did typechecking succeed?") } } Intrinsic::Powf => binary_float_intrinsic!(intrinsic, constants, powf), Intrinsic::Powi => { if let Constant::Integer32(p) = constants[1] { if let Constant::Float32(v) = constants[0] { ConstantLattice::Constant(Constant::Float32( ordered_float::OrderedFloat(v.powi(*p)), )) } else if let Constant::Float64(v) = constants[0] { ConstantLattice::Constant(Constant::Float64( ordered_float::OrderedFloat(v.powi(*p)), )) } else { panic!("Unsupported combination of intrinsic powi and constant value. Did typechecking succeed?") } } else { panic!("Unsupported combination of intrinsic powi and constant values. Did typechecking succeed?") } } Intrinsic::Round => unary_float_intrinsic!(intrinsic, constants, round), Intrinsic::Sin => unary_float_intrinsic!(intrinsic, constants, sin), Intrinsic::Sinh => unary_float_intrinsic!(intrinsic, constants, sinh), Intrinsic::Sqrt => unary_float_intrinsic!(intrinsic, constants, sqrt), Intrinsic::Tan => unary_float_intrinsic!(intrinsic, constants, tan), Intrinsic::Tanh => unary_float_intrinsic!(intrinsic, constants, tanh), }; } CCPLattice { reachability: new_reachability, constant: new_constant, } } Node::LibraryCall { library_function: _, args, ty: _, device: _, } => CCPLattice { reachability: args.iter().fold(ReachabilityLattice::bottom(), |val, id| { ReachabilityLattice::join(&val, &inputs[id.idx()].reachability) }), constant: ConstantLattice::bottom(), }, Node::Read { collect, indices } => { let mut reachability = inputs[collect.idx()].reachability.clone(); for index in indices.iter() { if let Index::Position(positions) = index { for position in positions.iter() { reachability = ReachabilityLattice::join( &reachability, &inputs[position.idx()].reachability, ); } } } CCPLattice { reachability, constant: ConstantLattice::bottom(), } } // Control projection handles reachability when following an if or match. Node::ControlProjection { control, selection } => match &editor.func().nodes[control.idx()] { Node::If { control: _, cond } => { let cond_constant = &inputs[cond.idx()].constant; let if_reachability = &inputs[control.idx()].reachability; let if_constant = &inputs[control.idx()].constant; let new_reachability = if cond_constant.is_top() { ReachabilityLattice::top() } else if let ConstantLattice::Constant(cons) = cond_constant { if let Constant::Boolean(val) = cons { if *val && *selection == 0 { // If condition is true and this is the false // branch, then unreachable. ReachabilityLattice::top() } else if !val && *selection == 1 { // If condition is true and this is the true branch, // then unreachable. ReachabilityLattice::top() } else { if_reachability.clone() } } else { panic!("Attempted to interpret Read node, where corresponding if node has a non-boolean constant input. Did typechecking succeed?") } } else { if_reachability.clone() }; CCPLattice { reachability: new_reachability, constant: if_constant.clone(), } } Node::Match { control: _, sum } => { let sum_constant = &inputs[sum.idx()].constant; let if_reachability = &inputs[control.idx()].reachability; let if_constant = &inputs[control.idx()].constant; let new_reachability = if sum_constant.is_top() { ReachabilityLattice::top() } else if let ConstantLattice::Constant(cons) = sum_constant { if let Constant::Summation(_, variant, _) = cons { if *variant as usize != *selection { // If match variant is not the same as this branch, // then unreachable. ReachabilityLattice::top() } else { if_reachability.clone() } } else { panic!("Attempted to interpret projection node, where corresponding match node has a non-summation constant input. Did typechecking succeed?") } } else { if_reachability.clone() }; CCPLattice { reachability: new_reachability, constant: if_constant.clone(), } } _ => panic!("Projection predecessor can only be an if or match node."), }, // Write is uninterpreted for now. Node::Write { collect, data, indices, } => { let mut reachability = ReachabilityLattice::join( &inputs[collect.idx()].reachability, &inputs[data.idx()].reachability, ); for index in indices.iter() { if let Index::Position(positions) = index { for position in positions.iter() { reachability = ReachabilityLattice::join( &reachability, &inputs[position.idx()].reachability, ); } } } CCPLattice { reachability, constant: ConstantLattice::bottom(), } } Node::Undef { ty: _ } => CCPLattice { reachability: ReachabilityLattice::bottom(), constant: ConstantLattice::top(), }, } }