Skip to content

Commit d6efc78

Browse files
authored
Initial support for overlapping LiveRanges (#122)
Relax the restrictions on liverange overlap for VRegs. #122
1 parent 1b4287b commit d6efc78

File tree

8 files changed

+414
-291
lines changed

8 files changed

+414
-291
lines changed

src/ion/data_structures.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct CodeRange {
3838
impl CodeRange {
3939
#[inline(always)]
4040
pub fn is_empty(&self) -> bool {
41-
self.from == self.to
41+
self.from >= self.to
4242
}
4343
#[inline(always)]
4444
pub fn contains(&self, other: &Self) -> bool {
@@ -64,6 +64,15 @@ impl CodeRange {
6464
to: pos.next(),
6565
}
6666
}
67+
68+
/// Join two [CodeRange] values together, producing a [CodeRange] that includes both.
69+
#[inline(always)]
70+
pub fn join(&self, other: CodeRange) -> Self {
71+
CodeRange {
72+
from: self.from.min(other.from),
73+
to: self.to.max(other.to),
74+
}
75+
}
6776
}
6877

6978
impl core::cmp::PartialOrd for CodeRange {
@@ -153,6 +162,9 @@ impl LiveRange {
153162
}
154163
#[inline(always)]
155164
pub fn uses_spill_weight(&self) -> SpillWeight {
165+
// NOTE: the spill weight is technically stored in 29 bits, but we ignore the sign bit as
166+
// we will always be dealing with positive values. Thus we mask out the top 3 bits to
167+
// ensure that the sign bit is clear, then shift left by only two.
156168
let bits = (self.uses_spill_weight_and_flags & 0x1fff_ffff) << 2;
157169
SpillWeight::from_f32(f32::from_bits(bits))
158170
}
@@ -285,17 +297,22 @@ const fn no_bloat_capacity<T>() -> usize {
285297

286298
#[derive(Clone, Debug)]
287299
pub struct SpillSet {
288-
pub vregs: SmallVec<[VRegIndex; no_bloat_capacity::<VRegIndex>()]>,
289300
pub slot: SpillSlotIndex,
290301
pub reg_hint: PReg,
291302
pub class: RegClass,
292303
pub spill_bundle: LiveBundleIndex,
293304
pub required: bool,
294305
pub size: u8,
295306
pub splits: u8,
307+
308+
/// The aggregate [`CodeRange`] of all involved [`LiveRange`]s. The effect of this abstraction
309+
/// is that we attempt to allocate one spill slot for the extent of a bundle. For fragmented
310+
/// bundles with lots of open space this abstraction is pessimistic, but when bundles are small
311+
/// or dense this yields similar results to tracking individual live ranges.
312+
pub range: CodeRange,
296313
}
297314

298-
pub(crate) const MAX_SPLITS_PER_SPILLSET: u8 = 10;
315+
pub(crate) const MAX_SPLITS_PER_SPILLSET: u8 = 2;
299316

300317
#[derive(Clone, Debug)]
301318
pub struct VRegData {
@@ -466,9 +483,22 @@ impl<'a, F: Function> Env<'a, F> {
466483
}
467484
}
468485

486+
#[derive(Clone, Debug)]
487+
pub struct SpillSetRanges {
488+
pub btree: BTreeMap<LiveRangeKey, SpillSetIndex>,
489+
}
490+
491+
impl SpillSetRanges {
492+
pub fn new() -> Self {
493+
Self {
494+
btree: BTreeMap::new(),
495+
}
496+
}
497+
}
498+
469499
#[derive(Clone, Debug)]
470500
pub struct SpillSlotData {
471-
pub ranges: LiveRangeSet,
501+
pub ranges: SpillSetRanges,
472502
pub slots: u32,
473503
pub alloc: Allocation,
474504
}

src/ion/liveranges.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,11 @@ impl<'a, F: Function> Env<'a, F> {
309309
pub fn add_liverange_to_preg(&mut self, range: CodeRange, reg: PReg) {
310310
trace!("adding liverange to preg: {:?} to {}", range, reg);
311311
let preg_idx = PRegIndex::new(reg.index());
312-
self.pregs[preg_idx.index()]
312+
let res = self.pregs[preg_idx.index()]
313313
.allocations
314314
.btree
315315
.insert(LiveRangeKey::from_range(&range), LiveRangeIndex::invalid());
316+
debug_assert!(res.is_none());
316317
}
317318

318319
pub fn is_live_in(&mut self, block: Block, vreg: VRegIndex) -> bool {

src/ion/merge.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,16 +214,10 @@ impl<'a, F: Function> Env<'a, F> {
214214
}
215215

216216
if self.bundles[from.index()].spillset != self.bundles[to.index()].spillset {
217-
let from_vregs = core::mem::replace(
218-
&mut self.spillsets[self.bundles[from.index()].spillset.index()].vregs,
219-
smallvec![],
220-
);
221-
let to_vregs = &mut self.spillsets[self.bundles[to.index()].spillset.index()].vregs;
222-
for vreg in from_vregs {
223-
if !to_vregs.contains(&vreg) {
224-
to_vregs.push(vreg);
225-
}
226-
}
217+
// Widen the range for the target spillset to include the one being merged in.
218+
let from_range = self.spillsets[self.bundles[from.index()].spillset.index()].range;
219+
let to_range = &mut self.spillsets[self.bundles[to.index()].spillset.index()].range;
220+
*to_range = to_range.join(from_range);
227221
}
228222

229223
if self.bundles[from.index()].cached_stack() {
@@ -246,6 +240,8 @@ impl<'a, F: Function> Env<'a, F> {
246240
}
247241

248242
let bundle = self.create_bundle();
243+
let mut range = self.vregs[vreg.index()].ranges.first().unwrap().range;
244+
249245
self.bundles[bundle.index()].ranges = self.vregs[vreg.index()].ranges.clone();
250246
trace!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
251247
for entry in &self.bundles[bundle.index()].ranges {
@@ -254,6 +250,7 @@ impl<'a, F: Function> Env<'a, F> {
254250
entry.index.index(),
255251
entry.range
256252
);
253+
range = range.join(entry.range);
257254
self.ranges[entry.index.index()].bundle = bundle;
258255
}
259256

@@ -291,14 +288,14 @@ impl<'a, F: Function> Env<'a, F> {
291288
let reg = self.vreg(vreg);
292289
let size = self.func.spillslot_size(reg.class()) as u8;
293290
self.spillsets.push(SpillSet {
294-
vregs: smallvec![vreg],
295291
slot: SpillSlotIndex::invalid(),
296292
size,
297293
required: false,
298294
class: reg.class(),
299295
reg_hint: PReg::invalid(),
300296
spill_bundle: LiveBundleIndex::invalid(),
301297
splits: 0,
298+
range,
302299
});
303300
self.bundles[bundle.index()].spillset = ssidx;
304301
}

0 commit comments

Comments
 (0)