Add all empty LRs to a single "spill bundle", to avoid many small bundles and excessive moves

This commit is contained in:
Chris Fallin
2021-05-19 22:12:22 -07:00
parent f56676fb8d
commit ce935c1040

View File

@@ -244,6 +244,7 @@ struct SpillSet {
slot: SpillSlotIndex, slot: SpillSlotIndex,
reg_hint: PReg, reg_hint: PReg,
class: RegClass, class: RegClass,
spill_bundle: LiveBundleIndex,
size: u8, size: u8,
} }
@@ -1858,6 +1859,7 @@ impl<'a, F: Function> Env<'a, F> {
size, size,
class: reg.class(), class: reg.class(),
reg_hint: PReg::invalid(), reg_hint: PReg::invalid(),
spill_bundle: LiveBundleIndex::invalid(),
}); });
self.bundles[bundle.index()].spillset = ssidx; self.bundles[bundle.index()].spillset = ssidx;
} }
@@ -2626,6 +2628,12 @@ impl<'a, F: Function> Env<'a, F> {
let spillset = self.bundles[bundle.index()].spillset; let spillset = self.bundles[bundle.index()].spillset;
// Get the spill bundle, which is the single bundle into which
// we place empty ranges (those with no uses). This may be
// `LiveBundleIndex::invalid()`, in which case we'll create it
// when we first need it.
let mut spill_bundle = self.spillsets[spillset.index()].spill_bundle;
let mut split_idx = 0; let mut split_idx = 0;
// Fast-forward past any splits that occur before or exactly // Fast-forward past any splits that occur before or exactly
@@ -2680,6 +2688,7 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!(" -> use at {:?}", u.pos); log::debug!(" -> use at {:?}", u.pos);
self.ranges[cur_lr.index()].uses.push(u); self.ranges[cur_lr.index()].uses.push(u);
} }
if self.ranges[cur_lr.index()].uses.len() > 0 {
self.ranges[cur_lr.index()].bundle = cur_bundle; self.ranges[cur_lr.index()].bundle = cur_bundle;
self.bundles[cur_bundle.index()] self.bundles[cur_bundle.index()]
.ranges .ranges
@@ -2687,6 +2696,33 @@ impl<'a, F: Function> Env<'a, F> {
range: cur_range, range: cur_range,
index: cur_lr, index: cur_lr,
}); });
} else {
if spill_bundle.is_invalid() {
spill_bundle = self.create_bundle();
log::debug!(
" -> allocating new spill-bundle for empty ranges: bundle{}",
spill_bundle.index()
);
self.bundles[spill_bundle.index()].spillset = spillset;
self.spillsets[spillset.index()].spill_bundle = spill_bundle;
self.spilled_bundles.push(spill_bundle);
}
// Range lists in empty-range bundles are not
// sorted until later (when we try to allocate
// regs or spillslots for them).
log::debug!(
" -> no uses in range{}; placing in empty-range spill-bundle bundle{}",
cur_lr.index(),
spill_bundle.index()
);
self.ranges[cur_lr.index()].bundle = spill_bundle;
self.bundles[spill_bundle.index()]
.ranges
.push(LiveRangeListEntry {
range: cur_range,
index: cur_lr,
});
}
break; break;
} }
@@ -2746,13 +2782,7 @@ impl<'a, F: Function> Env<'a, F> {
self.ranges[cur_lr.index()].range = existing_range; self.ranges[cur_lr.index()].range = existing_range;
self.ranges[new_lr.index()].vreg = self.ranges[cur_lr.index()].vreg; self.ranges[new_lr.index()].vreg = self.ranges[cur_lr.index()].vreg;
self.ranges[new_lr.index()].bundle = new_bundle; self.ranges[new_lr.index()].bundle = new_bundle;
self.ranges[cur_lr.index()].bundle = cur_bundle;
self.bundles[cur_bundle.index()]
.ranges
.push(LiveRangeListEntry {
range: existing_range,
index: cur_lr,
});
while let Some(u) = cur_uses.peek() { while let Some(u) = cur_uses.peek() {
if u.pos >= split { if u.pos >= split {
break; break;
@@ -2763,6 +2793,44 @@ impl<'a, F: Function> Env<'a, F> {
cur_uses.next(); cur_uses.next();
} }
if self.ranges[cur_lr.index()].uses.len() > 0 {
log::debug!(
" -> adding current LR {:?} to current bundle {:?}",
cur_lr,
cur_bundle
);
self.ranges[cur_lr.index()].bundle = cur_bundle;
self.bundles[cur_bundle.index()]
.ranges
.push(LiveRangeListEntry {
range: existing_range,
index: cur_lr,
});
} else {
if spill_bundle.is_invalid() {
spill_bundle = self.create_bundle();
log::debug!(
" -> allocating new spill-bundle for empty ranges: bundle{}",
spill_bundle.index()
);
self.bundles[spill_bundle.index()].spillset = spillset;
self.spillsets[spillset.index()].spill_bundle = spill_bundle;
self.spilled_bundles.push(spill_bundle);
}
log::debug!(
" -> no uses in range{}; placing in empty-range spill-bundle bundle{}",
cur_lr.index(),
spill_bundle.index()
);
self.ranges[cur_lr.index()].bundle = spill_bundle;
self.bundles[spill_bundle.index()]
.ranges
.push(LiveRangeListEntry {
range: cur_range,
index: cur_lr,
});
}
if self.annotations_enabled && log::log_enabled!(log::Level::Debug) { if self.annotations_enabled && log::log_enabled!(log::Level::Debug) {
self.annotate( self.annotate(
existing_range.to, existing_range.to,
@@ -2798,17 +2866,21 @@ impl<'a, F: Function> Env<'a, F> {
// Recompute weights and priorities of all bundles, and // Recompute weights and priorities of all bundles, and
// enqueue all split-bundles on the allocation queue. // enqueue all split-bundles on the allocation queue.
if self.bundles[bundle.index()].ranges.len() > 0 {
let prio = self.compute_bundle_prio(bundle); let prio = self.compute_bundle_prio(bundle);
self.bundles[bundle.index()].prio = prio; self.bundles[bundle.index()].prio = prio;
self.recompute_bundle_properties(bundle); self.recompute_bundle_properties(bundle);
self.allocation_queue.insert(bundle, prio as usize); self.allocation_queue.insert(bundle, prio as usize);
}
for &b in &new_bundles { for &b in &new_bundles {
if self.bundles[b.index()].ranges.len() > 0 {
let prio = self.compute_bundle_prio(b); let prio = self.compute_bundle_prio(b);
self.bundles[b.index()].prio = prio; self.bundles[b.index()].prio = prio;
self.recompute_bundle_properties(b); self.recompute_bundle_properties(b);
self.allocation_queue.insert(b, prio as usize); self.allocation_queue.insert(b, prio as usize);
} }
} }
}
fn process_bundle(&mut self, bundle: LiveBundleIndex) -> Result<(), RegAllocError> { fn process_bundle(&mut self, bundle: LiveBundleIndex) -> Result<(), RegAllocError> {
// Find any requirements: for every LR, for every def/use, gather // Find any requirements: for every LR, for every def/use, gather
@@ -3032,11 +3104,15 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!("allocating regs for spilled bundles"); log::debug!("allocating regs for spilled bundles");
for i in 0..self.spilled_bundles.len() { for i in 0..self.spilled_bundles.len() {
let bundle = self.spilled_bundles[i]; // don't borrow self let bundle = self.spilled_bundles[i]; // don't borrow self
let any_vreg = self.vreg_regs[self.ranges
[self.bundles[bundle.index()].ranges[0].index.index()] let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class;
.vreg
.index()]; // This may be an empty-range bundle whose ranges are not
let class = any_vreg.class(); // sorted; sort all range-lists again here.
self.bundles[bundle.index()]
.ranges
.sort_unstable_by_key(|entry| entry.range.from);
let mut success = false; let mut success = false;
self.stats.spill_bundle_reg_probes += 1; self.stats.spill_bundle_reg_probes += 1;
for preg in RegTraversalIter::new( for preg in RegTraversalIter::new(
@@ -3385,11 +3461,12 @@ impl<'a, F: Function> Env<'a, F> {
.unwrap_or_else(|| self.get_alloc_for_range(entry.index)); .unwrap_or_else(|| self.get_alloc_for_range(entry.index));
let range = entry.range; let range = entry.range;
log::debug!( log::debug!(
"apply_allocations: vreg {:?} LR {:?} with range {:?} has alloc {:?}", "apply_allocations: vreg {:?} LR {:?} with range {:?} has alloc {:?} (pinned {:?})",
vreg, vreg,
entry.index, entry.index,
range, range,
alloc alloc,
pinned_alloc,
); );
debug_assert!(alloc != Allocation::none()); debug_assert!(alloc != Allocation::none());