use crate::parser::ast::*; use crate::forms::*; use crate::instructions::*; use crate::iterators::*; use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; use std::collections::BTreeSet; use std::mem::swap; use std::rc::Rc; use std::vec::Vec; // labeled with chunk numbers. #[derive(Debug)] pub(crate) enum VarStatus { Perm(usize), Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _) } pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>; // Perm: 0 initially, a stack register once processed. // Temp: labeled with chunk_num and temp offset (unassigned if 0). #[derive(Debug)] pub(crate) enum VarData { Perm(usize), Temp(usize, usize, TempVarData), } impl VarData { pub(crate) fn as_reg_type(&self) -> RegType { match self { &VarData::Temp(_, r, _) => RegType::Temp(r), &VarData::Perm(r) => RegType::Perm(r), } } } #[derive(Debug)] pub(crate) struct TempVarData { pub(crate) last_term_arity: usize, pub(crate) use_set: OccurrenceSet, pub(crate) no_use_set: BTreeSet, pub(crate) conflict_set: BTreeSet, } impl TempVarData { pub(crate) fn new(last_term_arity: usize) -> Self { TempVarData { last_term_arity: last_term_arity, use_set: BTreeSet::new(), no_use_set: BTreeSet::new(), conflict_set: BTreeSet::new(), } } pub(crate) fn uses_reg(&self, reg: usize) -> bool { for &(_, nreg) in self.use_set.iter() { if reg == nreg { return true; } } return false; } pub(crate) fn populate_conflict_set(&mut self) { if self.last_term_arity > 0 { let arity = self.last_term_arity; let mut conflict_set: BTreeSet = (1..arity).collect(); for &(_, reg) in self.use_set.iter() { conflict_set.remove(®); } self.conflict_set = conflict_set; } } } type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); #[derive(Debug)] pub(crate) struct VariableFixtures<'a> { perm_vars: IndexMap, VariableFixture<'a>>, last_chunk_temp_vars: IndexSet>, } impl<'a> VariableFixtures<'a> { pub(crate) fn new() -> Self { VariableFixtures { perm_vars: IndexMap::new(), last_chunk_temp_vars: IndexSet::new(), } } pub(crate) fn insert(&mut self, var: Rc, vs: VariableFixture<'a>) { self.perm_vars.insert(var, vs); } pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc) { self.last_chunk_temp_vars.insert(var); } // computes no_use and conflict sets for all temp vars. pub(crate) fn populate_restricting_sets(&mut self) { // three stages: // 1. move the use sets of each variable to a local IndexMap, use_set // (iterate mutably, swap mutable refs). // 2. drain use_set. For each use set of U, add into the // no-use sets of appropriate variables T =/= U. // 3. Move the use sets back to their original locations in the fixture. // Compute the conflict set of u. // 1. let mut use_sets: IndexMap, OccurrenceSet> = IndexMap::new(); for (var, &mut (ref mut var_status, _)) in self.iter_mut() { if let &mut VarStatus::Temp(_, ref mut var_data) = var_status { let mut use_set = OccurrenceSet::new(); swap(&mut var_data.use_set, &mut use_set); use_sets.insert((*var).clone(), use_set); } } for (u, use_set) in use_sets.drain(..) { // 2. for &(term_loc, reg) in use_set.iter() { if let GenContext::Last(cn_u) = term_loc { for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() { if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status { if cn_u == cn_t && *u != ***t { if !t_data.uses_reg(reg) { t_data.no_use_set.insert(reg); } } } } } } // 3. match self.get_mut(u).unwrap() { &mut (VarStatus::Temp(_, ref mut u_data), _) => { u_data.use_set = use_set; u_data.populate_conflict_set(); } _ => {} }; } } fn get_mut(&mut self, u: Rc) -> Option<&mut VariableFixture<'a>> { self.perm_vars.get_mut(&u) } fn iter_mut(&mut self) -> indexmap::map::IterMut, VariableFixture<'a>> { self.perm_vars.iter_mut() } fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) { match term_loc { GenContext::Head | GenContext::Last(_) => { tvd.use_set.insert((term_loc, arg_c)); } _ => {} }; } pub(crate) fn vars_above_threshold(&self, index: usize) -> usize { let mut var_count = 0; for &(ref var_status, _) in self.values() { if let &VarStatus::Perm(i) = var_status { if i > index { var_count += 1; } } } var_count } pub(crate) fn mark_vars_in_chunk(&mut self, iter: I, lt_arity: usize, term_loc: GenContext) where I: Iterator>, { let chunk_num = term_loc.chunk_num(); let mut arg_c = 1; for term_ref in iter { if let &TermRef::Var(lvl, cell, ref var) = &term_ref { let mut status = self.perm_vars.swap_remove(var).unwrap_or(( VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)), Vec::new(), )); status.1.push(cell); match status.0 { VarStatus::Temp(cn, ref mut tvd) if cn == chunk_num => { if let Level::Shallow = lvl { self.record_temp_info(tvd, arg_c, term_loc); } } _ => status.0 = VarStatus::Perm(chunk_num), }; self.perm_vars.insert(var.clone(), status); } if let Level::Shallow = term_ref.level() { arg_c += 1; } } } pub(crate) fn into_iter(self) -> indexmap::map::IntoIter, VariableFixture<'a>> { self.perm_vars.into_iter() } fn values(&self) -> indexmap::map::Values, VariableFixture<'a>> { self.perm_vars.values() } pub(crate) fn size(&self) -> usize { self.perm_vars.len() } pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) { let mut values_vec: Vec<_> = self .values() .filter_map(|ref v| match &v.0 { &VarStatus::Perm(i) => Some((i, &v.1)), _ => None, }) .collect(); values_vec.sort_by_key(|ref v| v.0); let offset = has_deep_cuts as usize; for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() { for cell in cells { cell.set(VarReg::Norm(RegType::Perm(i + 1 + offset))); } } } } #[derive(Debug)] pub(crate) struct UnsafeVarMarker { pub(crate) unsafe_vars: IndexMap, pub(crate) safe_vars: IndexSet, } impl UnsafeVarMarker { pub(crate) fn new() -> Self { UnsafeVarMarker { unsafe_vars: IndexMap::new(), safe_vars: IndexSet::new(), } } pub(crate) fn from_safe_vars(safe_vars: IndexSet) -> Self { UnsafeVarMarker { unsafe_vars: IndexMap::new(), safe_vars, } } pub(crate) fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { match query_instr { &Instruction::PutVariable(r @ RegType::Temp(_), _) | &Instruction::SetVariable(r) => { self.safe_vars.insert(r); true } _ => false, } } pub(crate) fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { match query_instr { &Instruction::PutValue(r @ RegType::Perm(_), _) | &Instruction::SetValue(r) => { let p = self.unsafe_vars.entry(r).or_insert(0); *p = phase; } _ => {} } } pub(crate) fn mark_unsafe_vars(&mut self, query_instr: &mut Instruction, phase: usize) { match query_instr { &mut Instruction::PutValue(RegType::Perm(i), arg) => { if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) { if p == phase { *query_instr = Instruction::PutUnsafeValue(i, arg); self.safe_vars.insert(RegType::Perm(i)); } else { self.unsafe_vars.insert(RegType::Perm(i), p); } } } &mut Instruction::SetValue(r) => { if !self.safe_vars.contains(&r) { *query_instr = Instruction::SetLocalValue(r); self.safe_vars.insert(r); self.unsafe_vars.remove(&r); } } _ => {} } } }