merge bundles much faster by just concatenating range-lists and unstable-sorting, rather than a merge-sort-like traversal. Rust stdlib sort is very optimized. clang.wasm 9.1s -> 6.8s now.

This commit is contained in:
Chris Fallin
2021-06-03 17:34:19 -07:00
parent 6a0739b62a
commit 00e4240c93

View File

@@ -264,6 +264,16 @@ impl LiveBundle {
self.spill_weight_and_props & (1 << 29) != 0 self.spill_weight_and_props & (1 << 29) != 0
} }
#[inline(always)]
fn set_cached_fixed(&mut self) {
self.spill_weight_and_props |= 1 << 30;
}
#[inline(always)]
fn set_cached_stack(&mut self) {
self.spill_weight_and_props |= 1 << 29;
}
#[inline(always)] #[inline(always)]
fn cached_spill_weight(&self) -> u32 { fn cached_spill_weight(&self) -> u32 {
self.spill_weight_and_props & ((1 << 29) - 1) self.spill_weight_and_props & ((1 << 29) - 1)
@@ -2071,6 +2081,11 @@ impl<'a, F: Function> Env<'a, F> {
} }
// Check for a requirements conflict. // Check for a requirements conflict.
if self.bundles[from.index()].cached_stack()
|| self.bundles[from.index()].cached_fixed()
|| self.bundles[to.index()].cached_stack()
|| self.bundles[to.index()].cached_fixed()
{
let req = self let req = self
.compute_requirement(from) .compute_requirement(from)
.merge(self.compute_requirement(to)); .merge(self.compute_requirement(to));
@@ -2078,6 +2093,7 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!(" -> conflicting requirements; aborting merge"); log::debug!(" -> conflicting requirements; aborting merge");
return false; return false;
} }
}
log::debug!(" -> committing to merge"); log::debug!(" -> committing to merge");
@@ -2085,8 +2101,6 @@ impl<'a, F: Function> Env<'a, F> {
// them! We do this with a merge-sort-like scan over both // them! We do this with a merge-sort-like scan over both
// lists, building a new range list and replacing the list on // lists, building a new range list and replacing the list on
// `to` when we're done. // `to` when we're done.
let mut idx_from = 0;
let mut idx_to = 0;
if ranges_from.is_empty() { if ranges_from.is_empty() {
// `from` bundle is empty -- trivial merge. // `from` bundle is empty -- trivial merge.
log::debug!(" -> from bundle{} is empty; trivial merge", from.index()); log::debug!(" -> from bundle{} is empty; trivial merge", from.index());
@@ -2115,52 +2129,47 @@ impl<'a, F: Function> Env<'a, F> {
} }
self.bundles[to.index()].ranges = list; self.bundles[to.index()].ranges = list;
if self.bundles[from.index()].cached_stack() {
self.bundles[to.index()].set_cached_stack();
}
if self.bundles[from.index()].cached_fixed() {
self.bundles[to.index()].set_cached_fixed();
}
return true; return true;
} }
// Two non-empty lists of LiveRanges: traverse both simultaneously and
// merge ranges into `merged`.
let mut merged: LiveRangeList = smallvec![];
log::debug!( log::debug!(
"merging: ranges_from = {:?} ranges_to = {:?}", "merging: ranges_from = {:?} ranges_to = {:?}",
ranges_from, ranges_from,
ranges_to ranges_to
); );
while idx_from < ranges_from.len() || idx_to < ranges_to.len() {
if idx_from < ranges_from.len() && idx_to < ranges_to.len() { // Two non-empty lists of LiveRanges: concatenate and
if ranges_from[idx_from].range.from <= ranges_to[idx_to].range.from { // sort. This is faster than a mergesort-like merge into a new
self.ranges[ranges_from[idx_from].index.index()].bundle = to; // list, empirically.
merged.push(ranges_from[idx_from]); let from_list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
idx_from += 1; for entry in &from_list {
} else {
merged.push(ranges_to[idx_to]);
idx_to += 1;
}
} else if idx_from < ranges_from.len() {
for entry in &ranges_from[idx_from..] {
self.ranges[entry.index.index()].bundle = to; self.ranges[entry.index.index()].bundle = to;
} }
merged.extend_from_slice(&ranges_from[idx_from..]); self.bundles[to.index()]
break; .ranges
} else { .extend_from_slice(&from_list[..]);
assert!(idx_to < ranges_to.len()); self.bundles[to.index()]
merged.extend_from_slice(&ranges_to[idx_to..]); .ranges
break; .sort_unstable_by_key(|entry| entry.range.from);
}
}
#[cfg(debug_assertions)] if self.annotations_enabled {
{ log::debug!("merging: merged = {:?}", self.bundles[to.index()].ranges);
log::debug!("merging: merged = {:?}", merged);
let mut last_range = None; let mut last_range = None;
for entry in &merged { for i in 0..self.bundles[to.index()].ranges.len() {
let entry = self.bundles[to.index()].ranges[i];
if last_range.is_some() { if last_range.is_some() {
assert!(last_range.unwrap() < entry.range); assert!(last_range.unwrap() < entry.range);
} }
last_range = Some(entry.range); last_range = Some(entry.range);
if self.ranges[entry.index.index()].bundle == from { if self.ranges[entry.index.index()].bundle == from {
if self.annotations_enabled {
self.annotate( self.annotate(
entry.range.from, entry.range.from,
format!( format!(
@@ -2172,7 +2181,6 @@ impl<'a, F: Function> Env<'a, F> {
), ),
); );
} }
}
log::debug!( log::debug!(
" -> merged result for bundle{}: range{}", " -> merged result for bundle{}: range{}",
@@ -2182,9 +2190,6 @@ impl<'a, F: Function> Env<'a, F> {
} }
} }
self.bundles[to.index()].ranges = merged;
self.bundles[from.index()].ranges.clear();
if self.bundles[from.index()].spillset != self.bundles[to.index()].spillset { if self.bundles[from.index()].spillset != self.bundles[to.index()].spillset {
let from_vregs = std::mem::replace( let from_vregs = std::mem::replace(
&mut self.spillsets[self.bundles[from.index()].spillset.index()].vregs, &mut self.spillsets[self.bundles[from.index()].spillset.index()].vregs,
@@ -2198,6 +2203,13 @@ impl<'a, F: Function> Env<'a, F> {
} }
} }
if self.bundles[from.index()].cached_stack() {
self.bundles[to.index()].set_cached_stack();
}
if self.bundles[from.index()].cached_fixed() {
self.bundles[to.index()].set_cached_fixed();
}
true true
} }
@@ -2239,6 +2251,28 @@ impl<'a, F: Function> Env<'a, F> {
self.ranges[entry.index.index()].bundle = bundle; self.ranges[entry.index.index()].bundle = bundle;
} }
let mut fixed = false;
let mut stack = false;
for entry in &self.bundles[bundle.index()].ranges {
for u in &self.ranges[entry.index.index()].uses {
if let OperandPolicy::FixedReg(_) = u.operand.policy() {
fixed = true;
}
if let OperandPolicy::Stack = u.operand.policy() {
stack = true;
}
if fixed && stack {
break;
}
}
}
if fixed {
self.bundles[bundle.index()].set_cached_fixed();
}
if stack {
self.bundles[bundle.index()].set_cached_stack();
}
// Create a spillslot for this bundle. // Create a spillslot for this bundle.
let ssidx = SpillSetIndex::new(self.spillsets.len()); let ssidx = SpillSetIndex::new(self.spillsets.len());
let reg = self.vreg_regs[vreg.index()]; let reg = self.vreg_regs[vreg.index()];