Cleanup: split allocator implemntation into 11 files of more reasonable size.
This commit is contained in:
218
src/ion/spill.rs
Normal file
218
src/ion/spill.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* The following license applies to this file, which was initially
|
||||
* derived from the files `js/src/jit/BacktrackingAllocator.h` and
|
||||
* `js/src/jit/BacktrackingAllocator.cpp` in Mozilla Firefox:
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Since the initial port, the design has been substantially evolved
|
||||
* and optimized.
|
||||
*/
|
||||
|
||||
//! Spillslot allocation.
|
||||
|
||||
use super::{
|
||||
AllocRegResult, Env, LiveRangeKey, LiveRangeSet, PReg, PRegIndex, RegClass, RegTraversalIter,
|
||||
SpillSetIndex, SpillSlotData, SpillSlotIndex, SpillSlotList,
|
||||
};
|
||||
use crate::{Allocation, Function, SpillSlot};
|
||||
|
||||
impl<'a, F: Function> Env<'a, F> {
|
||||
pub fn try_allocating_regs_for_spilled_bundles(&mut self) {
|
||||
log::debug!("allocating regs for spilled bundles");
|
||||
for i in 0..self.spilled_bundles.len() {
|
||||
let bundle = self.spilled_bundles[i]; // don't borrow self
|
||||
|
||||
let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class;
|
||||
let hint = self.spillsets[self.bundles[bundle.index()].spillset.index()].reg_hint;
|
||||
|
||||
// This may be an empty-range bundle whose ranges are not
|
||||
// sorted; sort all range-lists again here.
|
||||
self.bundles[bundle.index()]
|
||||
.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)
|
||||
{
|
||||
log::debug!("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 {
|
||||
log::debug!(
|
||||
"spilling bundle {:?}: marking spillset {:?} as required",
|
||||
bundle,
|
||||
self.bundles[bundle.index()].spillset
|
||||
);
|
||||
self.spillsets[self.bundles[bundle.index()].spillset.index()].required = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spillslot_can_fit_spillset(
|
||||
&mut self,
|
||||
spillslot: SpillSlotIndex,
|
||||
spillset: SpillSetIndex,
|
||||
) -> bool {
|
||||
for &vreg in &self.spillsets[spillset.index()].vregs {
|
||||
for entry in &self.vregs[vreg.index()].ranges {
|
||||
if self.spillslots[spillslot.index()]
|
||||
.ranges
|
||||
.btree
|
||||
.contains_key(&LiveRangeKey::from_range(&entry.range))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn allocate_spillset_to_spillslot(
|
||||
&mut self,
|
||||
spillset: SpillSetIndex,
|
||||
spillslot: SpillSlotIndex,
|
||||
) {
|
||||
self.spillsets[spillset.index()].slot = spillslot;
|
||||
for i in 0..self.spillsets[spillset.index()].vregs.len() {
|
||||
// don't borrow self
|
||||
let vreg = self.spillsets[spillset.index()].vregs[i];
|
||||
log::debug!(
|
||||
"spillslot {:?} alloc'ed to spillset {:?}: vreg {:?}",
|
||||
spillslot,
|
||||
spillset,
|
||||
vreg,
|
||||
);
|
||||
for entry in &self.vregs[vreg.index()].ranges {
|
||||
log::debug!(
|
||||
"spillslot {:?} getting range {:?} from LR {:?} from vreg {:?}",
|
||||
spillslot,
|
||||
entry.range,
|
||||
entry.index,
|
||||
vreg,
|
||||
);
|
||||
self.spillslots[spillslot.index()]
|
||||
.ranges
|
||||
.btree
|
||||
.insert(LiveRangeKey::from_range(&entry.range), entry.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_spillslots(&mut self) {
|
||||
for spillset in 0..self.spillsets.len() {
|
||||
log::debug!("allocate spillslot: {}", spillset);
|
||||
let spillset = SpillSetIndex::new(spillset);
|
||||
if !self.spillsets[spillset.index()].required {
|
||||
continue;
|
||||
}
|
||||
// Get or create the spillslot list for this size.
|
||||
let size = self.spillsets[spillset.index()].size as usize;
|
||||
if size >= self.slots_by_size.len() {
|
||||
self.slots_by_size.resize(
|
||||
size + 1,
|
||||
SpillSlotList {
|
||||
first_spillslot: SpillSlotIndex::invalid(),
|
||||
last_spillslot: SpillSlotIndex::invalid(),
|
||||
},
|
||||
);
|
||||
}
|
||||
// Try a few existing spillslots.
|
||||
let mut spillslot_iter = self.slots_by_size[size].first_spillslot;
|
||||
let mut first_slot = SpillSlotIndex::invalid();
|
||||
let mut prev = SpillSlotIndex::invalid();
|
||||
let mut success = false;
|
||||
for _attempt in 0..10 {
|
||||
if spillslot_iter.is_invalid() {
|
||||
break;
|
||||
}
|
||||
if spillslot_iter == first_slot {
|
||||
// We've started looking at slots we placed at the end; end search.
|
||||
break;
|
||||
}
|
||||
if first_slot.is_invalid() {
|
||||
first_slot = spillslot_iter;
|
||||
}
|
||||
|
||||
if self.spillslot_can_fit_spillset(spillslot_iter, spillset) {
|
||||
self.allocate_spillset_to_spillslot(spillset, spillslot_iter);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
// Remove the slot and place it at the end of the respective list.
|
||||
let next = self.spillslots[spillslot_iter.index()].next_spillslot;
|
||||
if prev.is_valid() {
|
||||
self.spillslots[prev.index()].next_spillslot = next;
|
||||
} else {
|
||||
self.slots_by_size[size].first_spillslot = next;
|
||||
}
|
||||
if !next.is_valid() {
|
||||
self.slots_by_size[size].last_spillslot = prev;
|
||||
}
|
||||
|
||||
let last = self.slots_by_size[size].last_spillslot;
|
||||
if last.is_valid() {
|
||||
self.spillslots[last.index()].next_spillslot = spillslot_iter;
|
||||
} else {
|
||||
self.slots_by_size[size].first_spillslot = spillslot_iter;
|
||||
}
|
||||
self.slots_by_size[size].last_spillslot = spillslot_iter;
|
||||
|
||||
prev = spillslot_iter;
|
||||
spillslot_iter = next;
|
||||
}
|
||||
|
||||
if !success {
|
||||
// Allocate a new spillslot.
|
||||
let spillslot = SpillSlotIndex::new(self.spillslots.len());
|
||||
let next = self.slots_by_size[size].first_spillslot;
|
||||
self.spillslots.push(SpillSlotData {
|
||||
ranges: LiveRangeSet::new(),
|
||||
next_spillslot: next,
|
||||
alloc: Allocation::none(),
|
||||
class: self.spillsets[spillset.index()].class,
|
||||
});
|
||||
self.slots_by_size[size].first_spillslot = spillslot;
|
||||
if !next.is_valid() {
|
||||
self.slots_by_size[size].last_spillslot = spillslot;
|
||||
}
|
||||
|
||||
self.allocate_spillset_to_spillslot(spillset, spillslot);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign actual slot indices to spillslots.
|
||||
for i in 0..self.spillslots.len() {
|
||||
self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].class);
|
||||
}
|
||||
|
||||
log::debug!("spillslot allocator done");
|
||||
}
|
||||
|
||||
pub fn allocate_spillslot(&mut self, class: RegClass) -> Allocation {
|
||||
let size = self.func.spillslot_size(class) as u32;
|
||||
let mut offset = self.num_spillslots;
|
||||
// Align up to `size`.
|
||||
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, class))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user