#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
use crate::avl_tree::{AVLTree, AVL_NULL};
use crate::data_structures::{
cmp_range_frags, InstPoint, RangeFrag, SortedRangeFrags, SpillSlot, TypedIxVec, VirtualRange,
VirtualRangeIx,
};
use crate::union_find::UnionFindEquivClasses;
use crate::Function;
#[derive(Clone, PartialEq, PartialOrd)]
struct RangeFragAndRefness {
frag: RangeFrag,
is_ref: bool,
}
impl RangeFragAndRefness {
fn new(frag: RangeFrag, is_ref: bool) -> Self {
Self { frag, is_ref }
}
}
enum LogicalSpillSlot {
InUse {
size: u32,
tree: AVLTree<RangeFragAndRefness>,
},
Unavail,
}
impl LogicalSpillSlot {
fn is_Unavail(&self) -> bool {
match self {
LogicalSpillSlot::Unavail => true,
_ => false,
}
}
fn is_InUse(&self) -> bool {
!self.is_Unavail()
}
fn get_tree(&self) -> &AVLTree<RangeFragAndRefness> {
match self {
LogicalSpillSlot::InUse { ref tree, .. } => tree,
LogicalSpillSlot::Unavail => panic!("LogicalSpillSlot::get_tree"),
}
}
fn get_mut_tree(&mut self) -> &mut AVLTree<RangeFragAndRefness> {
match self {
LogicalSpillSlot::InUse { ref mut tree, .. } => tree,
LogicalSpillSlot::Unavail => panic!("LogicalSpillSlot::get_mut_tree"),
}
}
fn get_size(&self) -> u32 {
match self {
LogicalSpillSlot::InUse { size, .. } => *size,
LogicalSpillSlot::Unavail => panic!("LogicalSpillSlot::get_size"),
}
}
fn get_refness_at_inst_point(&self, pt: InstPoint) -> Option<bool> {
match self {
LogicalSpillSlot::InUse { size: 1, tree } => {
let mut root = tree.root;
while root != AVL_NULL {
let root_node = &tree.pool[root as usize];
let root_item = &root_node.item;
if pt < root_item.frag.first {
root = root_node.left;
} else if root_item.frag.last < pt {
root = root_node.right;
} else {
return Some(root_item.is_ref);
}
}
None
}
LogicalSpillSlot::InUse { .. } | LogicalSpillSlot::Unavail => {
None
}
}
}
}
#[inline(always)]
fn ssal_is_add_frag_possible(tree: &AVLTree<RangeFragAndRefness>, frag: &RangeFrag) -> bool {
let mut root = tree.root;
while root != AVL_NULL {
let root_node = &tree.pool[root as usize];
let root_item = &root_node.item;
if frag.last < root_item.frag.first {
root = root_node.left;
} else if root_item.frag.last < frag.first {
root = root_node.right;
} else {
return false;
}
}
true
}
fn ssal_is_add_possible(tree: &AVLTree<RangeFragAndRefness>, frags: &SortedRangeFrags) -> bool {
for frag in &frags.frags {
if !ssal_is_add_frag_possible(&tree, frag) {
return false;
}
}
true
}
fn ssal_add_if_possible(tree: &mut AVLTree<RangeFragAndRefness>, frags: &SortedRangeFrags) -> bool {
if !ssal_is_add_possible(tree, frags) {
return false;
}
for frag in &frags.frags {
let inserted = tree.insert(
RangeFragAndRefness::new(frag.clone(), false),
Some(&|item1: RangeFragAndRefness, item2: RangeFragAndRefness| {
cmp_range_frags(&item1.frag, &item2.frag)
}),
);
assert!(inserted);
}
true
}
fn ssal_mark_frags_as_reftyped(tree: &mut AVLTree<RangeFragAndRefness>, frags: &SortedRangeFrags) {
for frag in &frags.frags {
let del_this = RangeFragAndRefness::new(frag.clone(), false);
let add_this = RangeFragAndRefness::new(frag.clone(), true);
let replaced_ok = tree.find_and_replace(
del_this,
add_this,
&|item1: RangeFragAndRefness, item2: RangeFragAndRefness| {
cmp_range_frags(&item1.frag, &item2.frag)
},
);
assert!(replaced_ok);
}
}
pub struct SpillSlotAllocator {
slots: Vec<LogicalSpillSlot>,
}
impl SpillSlotAllocator {
pub fn new() -> Self {
Self { slots: vec![] }
}
pub fn num_slots_in_use(&self) -> usize {
self.slots.len()
}
fn add_new_slot(&mut self, req_size: u32) -> u32 {
assert!(req_size == 1 || req_size == 2 || req_size == 4 || req_size == 8);
while self.slots.len() % (req_size as usize) != 0 {
self.slots.push(LogicalSpillSlot::Unavail);
}
let dflt = RangeFragAndRefness::new(RangeFrag::invalid_value(), false);
let tree = AVLTree::<RangeFragAndRefness>::new(dflt);
let res = self.slots.len() as u32;
self.slots.push(LogicalSpillSlot::InUse {
size: req_size,
tree,
});
for _ in 1..req_size {
self.slots.push(LogicalSpillSlot::Unavail);
}
assert!(self.slots.len() % (req_size as usize) == 0);
res
}
pub fn alloc_spill_slots<F: Function>(
&mut self,
vlr_slot_env: &mut TypedIxVec<VirtualRangeIx, Option<SpillSlot>>,
func: &F,
vlr_env: &TypedIxVec<VirtualRangeIx, VirtualRange>,
vlrEquivClasses: &UnionFindEquivClasses<VirtualRangeIx>,
vlrix: VirtualRangeIx,
) {
let is_ref = vlr_env[vlrix].is_ref;
for cand_vlrix in vlrEquivClasses.equiv_class_elems_iter(vlrix) {
assert!(vlr_slot_env[cand_vlrix].is_none());
assert!(vlr_env[cand_vlrix].is_ref == is_ref);
}
let vlrix_vreg = vlr_env[vlrix].vreg;
let req_size = func.get_spillslot_size(vlrix_vreg.get_class(), vlrix_vreg);
assert!(req_size == 1 || req_size == 2 || req_size == 4 || req_size == 8);
if is_ref {
assert!(req_size == 1);
}
let search_start_slotno: u32 = {
let window = 8;
if self.slots.len() >= window {
(self.slots.len() - window) as u32
} else {
0
}
};
let mut mb_chosen_slotno: Option<u32> = None;
for cand_slot_no in search_start_slotno..self.slots.len() as u32 {
let cand_slot = &self.slots[cand_slot_no as usize];
if !cand_slot.is_InUse() {
continue;
}
if cand_slot.get_size() != req_size {
continue;
}
let tree = &cand_slot.get_tree();
assert!(mb_chosen_slotno.is_none());
let mut all_cands_fit_individually = true;
for cand_vlrix in vlrEquivClasses.equiv_class_elems_iter(vlrix) {
let cand_vlr = &vlr_env[cand_vlrix];
let this_cand_fits = ssal_is_add_possible(&tree, &cand_vlr.sorted_frags);
if !this_cand_fits {
all_cands_fit_individually = false;
break;
}
}
if !all_cands_fit_individually {
continue;
}
mb_chosen_slotno = Some(cand_slot_no);
break;
}
let chosen_slotno: u32 = if mb_chosen_slotno.is_none() {
self.add_new_slot(req_size)
} else {
mb_chosen_slotno.unwrap()
};
let mut _all_in_chosen = true;
'pass2_per_equiv_class: for cand_vlrix in vlrEquivClasses.equiv_class_elems_iter(vlrix) {
let cand_vlr = &vlr_env[cand_vlrix];
let mut tree = self.slots[chosen_slotno as usize].get_mut_tree();
let added = ssal_add_if_possible(&mut tree, &cand_vlr.sorted_frags);
if added {
vlr_slot_env[cand_vlrix] = Some(SpillSlot::new(chosen_slotno));
continue 'pass2_per_equiv_class;
}
_all_in_chosen = false;
for alt_slotno in search_start_slotno..self.slots.len() as u32 {
let alt_slot = &self.slots[alt_slotno as usize];
if !alt_slot.is_InUse() {
continue;
}
if alt_slot.get_size() != req_size {
continue;
}
if alt_slotno == chosen_slotno {
continue;
}
let mut tree = self.slots[alt_slotno as usize].get_mut_tree();
let added = ssal_add_if_possible(&mut tree, &cand_vlr.sorted_frags);
if added {
vlr_slot_env[cand_vlrix] = Some(SpillSlot::new(alt_slotno));
continue 'pass2_per_equiv_class;
}
}
let new_slotno = self.add_new_slot(req_size);
let mut tree = self.slots[new_slotno as usize].get_mut_tree();
let added = ssal_add_if_possible(&mut tree, &cand_vlr.sorted_frags);
if added {
vlr_slot_env[cand_vlrix] = Some(SpillSlot::new(new_slotno));
continue 'pass2_per_equiv_class;
}
panic!("SpillSlotAllocator: alloc_spill_slots: failed?!?!");
}
}
pub fn notify_spillage_of_reftyped_vlr(
&mut self,
slot_no: SpillSlot,
frags: &SortedRangeFrags,
) {
let slot_ix = slot_no.get_usize();
assert!(slot_ix < self.slots.len());
let slot = &mut self.slots[slot_ix];
match slot {
LogicalSpillSlot::InUse { size, tree } if *size == 1 => {
ssal_mark_frags_as_reftyped(tree, frags)
}
_ => panic!("SpillSlotAllocator::notify_spillage_of_reftyped_vlr: invalid slot"),
}
}
pub fn alloc_reftyped_spillslot_for_frag(&mut self, frag: RangeFrag) -> SpillSlot {
for i in 0..self.slots.len() {
match &mut self.slots[i] {
LogicalSpillSlot::InUse { size: 1, tree } => {
if ssal_is_add_frag_possible(&tree, &frag) {
let inserted = tree.insert(
RangeFragAndRefness::new(frag, true),
Some(&|item1: RangeFragAndRefness, item2: RangeFragAndRefness| {
cmp_range_frags(&item1.frag, &item2.frag)
}),
);
assert!(inserted);
return SpillSlot::new(i as u32);
}
}
LogicalSpillSlot::InUse { .. } | LogicalSpillSlot::Unavail => {
}
}
}
self.add_new_slot(1 );
self.alloc_reftyped_spillslot_for_frag(frag) }
pub fn get_reftyped_spillslots_at_inst_point(&self, pt: InstPoint) -> Vec<SpillSlot> {
let mut res = Vec::<SpillSlot>::new();
for (i, slot) in self.slots.iter().enumerate() {
if slot.get_refness_at_inst_point(pt) == Some(true) {
res.push(SpillSlot::new(i as u32));
}
}
res
}
}