Coalesce live range intervals in adjacent EBBs

LiveRanges represent the live-in range of a value as a sorted
list of intervals. Each interval starts at an EBB and continues
to an instruction. Before this commit, the LiveRange would store
an interval for each EBB. This commit changes the representation
such that intervals continuing from one EBB to another are coalesced
into one.

Fixes #37.
This commit is contained in:
Mikko Perttunen
2017-02-14 14:04:03 +02:00
committed by Jakob Stoklund Olesen
parent f6391c57e8
commit 5a1d9561a7
3 changed files with 115 additions and 11 deletions

View File

@@ -102,6 +102,13 @@ impl ProgramOrder for Layout {
let b_seq = self.seq(b);
a_seq.cmp(&b_seq)
}
fn is_ebb_gap(&self, inst: Inst, ebb: Ebb) -> bool {
let i = &self.insts[inst];
let e = &self.ebbs[ebb];
i.next.is_none() && i.ebb == e.prev
}
}
// Private methods for dealing with sequence numbers.
@@ -1267,5 +1274,10 @@ mod tests {
assert_eq!(layout.cmp(e2, e2), Ordering::Equal);
assert_eq!(layout.cmp(e2, i2), Ordering::Less);
assert_eq!(layout.cmp(i3, i2), Ordering::Greater);
assert_eq!(layout.is_ebb_gap(i1, e2), true);
assert_eq!(layout.is_ebb_gap(i3, e1), true);
assert_eq!(layout.is_ebb_gap(i1, e1), false);
assert_eq!(layout.is_ebb_gap(i2, e1), false);
}
}

View File

@@ -94,6 +94,11 @@ pub trait ProgramOrder {
fn cmp<A, B>(&self, a: A, b: B) -> cmp::Ordering
where A: Into<ExpandedProgramPoint>,
B: Into<ExpandedProgramPoint>;
/// Is the range from `inst` to `ebb` just the gap between consecutive EBBs?
///
/// This returns true if `inst` is the terminator in the EBB immediately before `ebb`.
fn is_ebb_gap(&self, inst: Inst, ebb: Ebb) -> bool;
}
#[cfg(test)]

View File

@@ -173,6 +173,7 @@ pub struct LiveRange {
/// This represents a live-in interval for a single EBB, or a coalesced set of live-in intervals
/// for contiguous EBBs where all but the last live-in interval covers the whole EBB.
///
#[derive(Copy, Clone)]
struct Interval {
/// Interval starting point.
///
@@ -265,18 +266,57 @@ impl LiveRange {
Ok(n) => {
// We have an interval that contains `ebb`, so we can simply extend it.
self.liveins[n].extend_to(to, order);
// TODO: Check if this interval can be coalesced with the `n+1` one.
// If `to` is the terminator and the value lives in the successor EBB,
// coalesce the two intervals.
if let Some(next) = self.liveins.get(n + 1).cloned() {
if order.is_ebb_gap(to, next.begin) {
self.liveins[n].extend_to(next.end, order);
self.liveins.remove(n + 1);
}
}
false
}
Err(n) => {
// Insert a new live-in interval at `n`.
// TODO: Check if this interval can be coalesced with the `n-1` or `n+1` one (or
// both).
self.liveins.insert(n,
Interval {
begin: ebb,
end: to,
});
// Insert a new live-in interval at `n`, or coalesce to predecessor or successor
// if possible.
// Determine if the new live-in range touches the predecessor or successor range
// and can therefore be coalesced to them.
let (coalesce_prev, coalesce_next) = {
let prev = n.checked_sub(1).and_then(|i| self.liveins.get(i));
let next = self.liveins.get(n);
(prev.map_or(false, |prev| order.is_ebb_gap(prev.end, ebb)),
next.map_or(false, |next| order.is_ebb_gap(to, next.begin)))
};
match (coalesce_prev, coalesce_next) {
// Extend predecessor interval to cover new and successor intervals
(true, true) => {
let end = self.liveins[n].end;
self.liveins[n - 1].extend_to(end, order);
self.liveins.remove(n);
}
// Extend predecessor interval to cover new interval
(true, false) => {
self.liveins[n - 1].extend_to(to, order);
}
// Extend successor interval to cover new interval
(false, true) => {
self.liveins[n].begin = ebb;
}
// Cannot coalesce; insert new interval
(false, false) => {
self.liveins.insert(n,
Interval {
begin: ebb,
end: to,
});
}
}
true
}
}
@@ -344,7 +384,8 @@ mod tests {
// Dummy program order which simply compares indexes.
// It is assumed that EBBs have indexes that are multiples of 10, and instructions have indexes
// in between.
// in between. `is_ebb_gap` assumes that terminator instructions have indexes of the form
// ebb * 10 + 1. This is used in the coalesce test.
struct ProgOrder {}
impl ProgramOrder for ProgOrder {
@@ -363,6 +404,10 @@ mod tests {
let ib = idx(b.into());
ia.cmp(&ib)
}
fn is_ebb_gap(&self, inst: Inst, ebb: Ebb) -> bool {
inst.index() % 10 == 1 && ebb.index() / 10 == inst.index() / 10 + 1
}
}
impl ProgOrder {
@@ -528,5 +573,47 @@ mod tests {
assert_eq!(lr.livein_local_end(e20, PO), Some(i23));
}
// TODO: Add more tests that exercise the binary search and the coalescing algorithm.
#[test]
fn coalesce() {
let v0 = Value::new(0);
let i11 = Inst::new(11);
let e20 = Ebb::new(20);
let i21 = Inst::new(21);
let e30 = Ebb::new(30);
let i31 = Inst::new(31);
let e40 = Ebb::new(40);
let i41 = Inst::new(41);
let mut lr = LiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e30, i31, PO), true);
assert_eq!(lr.liveins.len(), 1);
// Coalesce to previous
assert_eq!(lr.extend_in_ebb(e40, i41, PO), true);
assert_eq!(lr.liveins.len(), 1);
assert_eq!(lr.liveins[0].begin, e30);
assert_eq!(lr.liveins[0].end, i41);
// Coalesce to next
assert_eq!(lr.extend_in_ebb(e20, i21, PO), true);
assert_eq!(lr.liveins.len(), 1);
assert_eq!(lr.liveins[0].begin, e20);
assert_eq!(lr.liveins[0].end, i41);
let mut lr = LiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e40, i41, PO), true);
assert_eq!(lr.liveins.len(), 1);
assert_eq!(lr.extend_in_ebb(e20, i21, PO), true);
assert_eq!(lr.liveins.len(), 2);
// Coalesce to previous and next
assert_eq!(lr.extend_in_ebb(e30, i31, PO), true);
assert_eq!(lr.liveins.len(), 1);
assert_eq!(lr.liveins[0].begin, e20);
assert_eq!(lr.liveins[0].end, i41);
}
// TODO: Add more tests that exercise the binary search algorithm.
}