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,12 +2081,18 @@ impl<'a, F: Function> Env<'a, F> {
} }
// Check for a requirements conflict. // Check for a requirements conflict.
let req = self if self.bundles[from.index()].cached_stack()
.compute_requirement(from) || self.bundles[from.index()].cached_fixed()
.merge(self.compute_requirement(to)); || self.bundles[to.index()].cached_stack()
if req == Requirement::Conflict { || self.bundles[to.index()].cached_fixed()
log::debug!(" -> conflicting requirements; aborting merge"); {
return false; let req = self
.compute_requirement(from)
.merge(self.compute_requirement(to));
if req == Requirement::Conflict {
log::debug!(" -> conflicting requirements; aborting merge");
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,63 +2129,57 @@ 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() {
if ranges_from[idx_from].range.from <= ranges_to[idx_to].range.from {
self.ranges[ranges_from[idx_from].index.index()].bundle = to;
merged.push(ranges_from[idx_from]);
idx_from += 1;
} 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;
}
merged.extend_from_slice(&ranges_from[idx_from..]);
break;
} else {
assert!(idx_to < ranges_to.len());
merged.extend_from_slice(&ranges_to[idx_to..]);
break;
}
}
#[cfg(debug_assertions)] // Two non-empty lists of LiveRanges: concatenate and
{ // sort. This is faster than a mergesort-like merge into a new
log::debug!("merging: merged = {:?}", merged); // list, empirically.
let from_list = std::mem::replace(&mut self.bundles[from.index()].ranges, smallvec![]);
for entry in &from_list {
self.ranges[entry.index.index()].bundle = to;
}
self.bundles[to.index()]
.ranges
.extend_from_slice(&from_list[..]);
self.bundles[to.index()]
.ranges
.sort_unstable_by_key(|entry| entry.range.from);
if self.annotations_enabled {
log::debug!("merging: merged = {:?}", self.bundles[to.index()].ranges);
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!( " MERGE range{} v{} from bundle{} to bundle{}",
" MERGE range{} v{} from bundle{} to bundle{}", entry.index.index(),
entry.index.index(), self.ranges[entry.index.index()].vreg.index(),
self.ranges[entry.index.index()].vreg.index(), from.index(),
from.index(), to.index(),
to.index(), ),
), );
);
}
} }
log::debug!( log::debug!(
@@ -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()];