use super::{
AllocRegResult, Env, LiveRangeKey, PReg, PRegIndex, RegTraversalIter, SpillSetIndex,
SpillSlotData, SpillSlotIndex,
};
use crate::{ion::data_structures::SpillSetRanges, Allocation, Function, SpillSlot};
impl<'a, F: Function> Env<'a, F> {
pub fn try_allocating_regs_for_spilled_bundles(&mut self) {
trace!("allocating regs for spilled bundles");
for i in 0..self.spilled_bundles.len() {
let bundle = self.spilled_bundles[i]; if self.bundles[bundle].ranges.is_empty() {
continue;
}
let class = self.spillsets[self.bundles[bundle].spillset].class;
let hint = self.spillsets[self.bundles[bundle].spillset].reg_hint;
self.bundles[bundle]
.ranges
.sort_unstable_by_key(|entry| entry.range.from);
let mut success = false;
self.stats.spill_bundle_reg_probes += 1;
for preg in
RegTraversalIter::new(self.env, class, hint, PReg::invalid(), bundle.index(), None)
{
trace!("trying bundle {:?} to preg {:?}", bundle, preg);
let preg_idx = PRegIndex::new(preg.index());
if let AllocRegResult::Allocated(_) =
self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None)
{
self.stats.spill_bundle_reg_success += 1;
success = true;
break;
}
}
if !success {
trace!(
"spilling bundle {:?}: marking spillset {:?} as required",
bundle,
self.bundles[bundle].spillset
);
self.spillsets[self.bundles[bundle].spillset].required = true;
}
}
}
pub fn spillslot_can_fit_spillset(
&mut self,
spillslot: SpillSlotIndex,
spillset: SpillSetIndex,
) -> bool {
!self.spillslots[spillslot.index()]
.ranges
.btree
.contains_key(&LiveRangeKey::from_range(&self.spillsets[spillset].range))
}
pub fn allocate_spillset_to_spillslot(
&mut self,
spillset: SpillSetIndex,
spillslot: SpillSlotIndex,
) {
self.spillsets[spillset].slot = spillslot;
let res = self.spillslots[spillslot.index()].ranges.btree.insert(
LiveRangeKey::from_range(&self.spillsets[spillset].range),
spillset,
);
debug_assert!(res.is_none());
}
pub fn allocate_spillslots(&mut self) {
const MAX_ATTEMPTS: usize = 10;
for spillset in 0..self.spillsets.len() {
trace!("allocate spillslot: {}", spillset);
let spillset = SpillSetIndex::new(spillset);
if !self.spillsets[spillset].required {
continue;
}
let class = self.spillsets[spillset].class as usize;
let mut i = self.slots_by_class[class].probe_start;
let mut success = false;
for _attempt in 0..core::cmp::min(self.slots_by_class[class].slots.len(), MAX_ATTEMPTS)
{
let spillslot = self.slots_by_class[class].slots[i];
if self.spillslot_can_fit_spillset(spillslot, spillset) {
self.allocate_spillset_to_spillslot(spillset, spillslot);
success = true;
self.slots_by_class[class].probe_start = i;
break;
}
i = self.slots_by_class[class].next_index(i);
}
if !success {
let spillslot = SpillSlotIndex::new(self.spillslots.len());
self.spillslots.push(SpillSlotData {
ranges: SpillSetRanges::new(),
alloc: Allocation::none(),
slots: self.func.spillslot_size(self.spillsets[spillset].class) as u32,
});
self.slots_by_class[class].slots.push(spillslot);
self.slots_by_class[class].probe_start = self.slots_by_class[class].slots.len() - 1;
self.allocate_spillset_to_spillslot(spillset, spillslot);
}
}
for i in 0..self.spillslots.len() {
self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].slots);
}
trace!("spillslot allocator done");
}
pub fn allocate_spillslot(&mut self, size: u32) -> Allocation {
let mut offset = self.num_spillslots;
debug_assert!(size.is_power_of_two());
offset = (offset + size - 1) & !(size - 1);
let slot = if self.func.multi_spillslot_named_by_last_slot() {
offset + size - 1
} else {
offset
};
offset += size;
self.num_spillslots = offset;
Allocation::stack(SpillSlot::new(slot as usize))
}
}