Split based on first conflict of lowest-weight conflict, not first conflict. Also stop scanning PRegs when max bundle weight in conflict bundle list exceeds current best option.

This commit is contained in:
Chris Fallin
2021-05-24 22:26:57 -07:00
parent 5b47462e0c
commit 3382f9a2e8

View File

@@ -24,7 +24,10 @@
- Split heuristics: - Split heuristics:
- Loop depth at split point? Split before entering more nested loop - Loop depth at split point? Split before entering more nested loop
- Split at earliest vs latest conflict -- study more - In general, consider 'weight' of split point as if it were
another use.
- Add weight to bundles according to progmoves
- Reduced spilling when spillslot is still "clean": - Reduced spilling when spillslot is still "clean":
- When we allocate spillsets, use the whole bundle of a given - When we allocate spillsets, use the whole bundle of a given
@@ -44,12 +47,6 @@
scan in a single range while resolving moves; in-edge makes scan in a single range while resolving moves; in-edge makes
dirty. dirty.
- Add weight to bundles according to progmoves
- Efficiency improvements:
- Record 'cheapest evict bundle so far' and stop scanning if
total evict cost exceeds that
- Avoid requiring two scratch regs: - Avoid requiring two scratch regs:
- Require machine impl to be able to (i) push a reg, (ii) pop a - Require machine impl to be able to (i) push a reg, (ii) pop a
reg; then generate a balanced pair of push/pop, using the stack reg; then generate a balanced pair of push/pop, using the stack
@@ -611,6 +608,7 @@ enum AllocRegResult {
Allocated(Allocation), Allocated(Allocation),
Conflict(LiveBundleVec), Conflict(LiveBundleVec),
ConflictWithFixed, ConflictWithFixed,
ConflictHighCost,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -2425,9 +2423,14 @@ impl<'a, F: Function> Env<'a, F> {
&mut self, &mut self,
bundle: LiveBundleIndex, bundle: LiveBundleIndex,
reg: PRegIndex, reg: PRegIndex,
// if the max bundle weight in the conflict set exceeds this
// cost (if provided), just return
// `AllocRegResult::ConflictHighCost`.
max_allowable_cost: Option<u32>,
) -> AllocRegResult { ) -> AllocRegResult {
log::debug!("try_to_allocate_bundle_to_reg: {:?} -> {:?}", bundle, reg); log::debug!("try_to_allocate_bundle_to_reg: {:?} -> {:?}", bundle, reg);
let mut conflicts = smallvec![]; let mut conflicts = smallvec![];
let mut max_conflict_weight = 0;
// Traverse the BTreeMap in order by requesting the whole // Traverse the BTreeMap in order by requesting the whole
// range spanned by the bundle and iterating over that // range spanned by the bundle and iterating over that
// concurrently with our ranges. Because our ranges are in // concurrently with our ranges. Because our ranges are in
@@ -2499,6 +2502,15 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!(" -> conflict bundle {:?}", conflict_bundle); log::debug!(" -> conflict bundle {:?}", conflict_bundle);
if !conflicts.iter().any(|b| *b == conflict_bundle) { if !conflicts.iter().any(|b| *b == conflict_bundle) {
conflicts.push(conflict_bundle); conflicts.push(conflict_bundle);
max_conflict_weight = std::cmp::max(
max_conflict_weight,
self.bundles[conflict_bundle.index()].cached_spill_weight(),
);
if max_allowable_cost.is_some()
&& max_conflict_weight > max_allowable_cost.unwrap()
{
return AllocRegResult::ConflictHighCost;
}
} }
} else { } else {
log::debug!(" -> conflict with fixed reservation"); log::debug!(" -> conflict with fixed reservation");
@@ -2898,13 +2910,12 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!("attempt {}, req {:?}", attempts, req); log::debug!("attempt {}, req {:?}", attempts, req);
debug_assert!(attempts < 100 * self.func.insts()); debug_assert!(attempts < 100 * self.func.insts());
let (conflicting_bundles, latest_first_conflict_point, latest_first_conflict_reg) = let (conflicting_bundles, first_conflict_point, first_conflict_reg) = match req {
match req {
Requirement::Fixed(preg) => { Requirement::Fixed(preg) => {
let preg_idx = PRegIndex::new(preg.index()); let preg_idx = PRegIndex::new(preg.index());
self.stats.process_bundle_reg_probes_fixed += 1; self.stats.process_bundle_reg_probes_fixed += 1;
log::debug!("trying fixed reg {:?}", preg_idx); log::debug!("trying fixed reg {:?}", preg_idx);
match self.try_to_allocate_bundle_to_reg(bundle, preg_idx) { match self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None) {
AllocRegResult::Allocated(alloc) => { AllocRegResult::Allocated(alloc) => {
self.stats.process_bundle_reg_success_fixed += 1; self.stats.process_bundle_reg_success_fixed += 1;
log::debug!(" -> allocated to fixed {:?}", preg_idx); log::debug!(" -> allocated to fixed {:?}", preg_idx);
@@ -2931,13 +2942,15 @@ impl<'a, F: Function> Env<'a, F> {
PReg::invalid(), PReg::invalid(),
) )
} }
AllocRegResult::ConflictHighCost => unreachable!(),
} }
} }
Requirement::Register(class) => { Requirement::Register(class) => {
// Scan all pregs and attempt to allocate. // Scan all pregs and attempt to allocate.
let mut lowest_cost_conflict_set: Option<LiveBundleVec> = None; let mut lowest_cost_conflict_set: Option<LiveBundleVec> = None;
let mut latest_first_conflict_point = ProgPoint::before(Inst::new(0)); let mut lowest_cost_conflict_cost: Option<u32> = None;
let mut latest_first_conflict_reg = PReg::invalid(); let mut lowest_cost_conflict_point = ProgPoint::before(Inst::new(0));
let mut lowest_cost_conflict_reg = PReg::invalid();
// Heuristic: start the scan for an available // Heuristic: start the scan for an available
// register at an offset influenced both by our // register at an offset influenced both by our
@@ -2963,7 +2976,12 @@ impl<'a, F: Function> Env<'a, F> {
self.stats.process_bundle_reg_probes_any += 1; self.stats.process_bundle_reg_probes_any += 1;
let preg_idx = PRegIndex::new(preg.index()); let preg_idx = PRegIndex::new(preg.index());
log::debug!("trying preg {:?}", preg_idx); log::debug!("trying preg {:?}", preg_idx);
match self.try_to_allocate_bundle_to_reg(bundle, preg_idx) {
match self.try_to_allocate_bundle_to_reg(
bundle,
preg_idx,
lowest_cost_conflict_cost,
) {
AllocRegResult::Allocated(alloc) => { AllocRegResult::Allocated(alloc) => {
self.stats.process_bundle_reg_success_any += 1; self.stats.process_bundle_reg_success_any += 1;
log::debug!(" -> allocated to any {:?}", preg_idx); log::debug!(" -> allocated to any {:?}", preg_idx);
@@ -2976,25 +2994,31 @@ impl<'a, F: Function> Env<'a, F> {
let first_conflict_point = let first_conflict_point =
self.bundles[bundles[0].index()].ranges[0].range.from; self.bundles[bundles[0].index()].ranges[0].range.from;
if first_conflict_point > latest_first_conflict_point {
latest_first_conflict_point = first_conflict_point;
latest_first_conflict_reg = preg;
}
if lowest_cost_conflict_set.is_none() { let cost = self.maximum_spill_weight_in_bundle_set(&bundles);
if lowest_cost_conflict_cost.is_none() {
lowest_cost_conflict_cost = Some(cost);
lowest_cost_conflict_set = Some(bundles); lowest_cost_conflict_set = Some(bundles);
} else if self.maximum_spill_weight_in_bundle_set(&bundles) lowest_cost_conflict_point = first_conflict_point;
< self.maximum_spill_weight_in_bundle_set( lowest_cost_conflict_reg = preg;
lowest_cost_conflict_set.as_ref().unwrap(), } else if cost < lowest_cost_conflict_cost.unwrap() {
) lowest_cost_conflict_cost = Some(cost);
{
lowest_cost_conflict_set = Some(bundles); lowest_cost_conflict_set = Some(bundles);
lowest_cost_conflict_point = first_conflict_point;
lowest_cost_conflict_reg = preg;
} }
} }
AllocRegResult::ConflictWithFixed => { AllocRegResult::ConflictWithFixed => {
log::debug!(" -> conflict with fixed alloc"); log::debug!(" -> conflict with fixed alloc");
// Simply don't consider as an option. // Simply don't consider as an option.
} }
AllocRegResult::ConflictHighCost => {
// Simply don't consider -- we already have
// a lower-cost conflict bundle option
// to evict.
continue;
}
} }
} }
@@ -3006,8 +3030,8 @@ impl<'a, F: Function> Env<'a, F> {
// split instead. // split instead.
( (
lowest_cost_conflict_set.unwrap_or(smallvec![]), lowest_cost_conflict_set.unwrap_or(smallvec![]),
latest_first_conflict_point, lowest_cost_conflict_point,
latest_first_conflict_reg, lowest_cost_conflict_reg,
) )
} }
@@ -3035,9 +3059,9 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!(" -> conflict set {:?}", conflicting_bundles); log::debug!(" -> conflict set {:?}", conflicting_bundles);
log::debug!( log::debug!(
" -> latest first conflict {:?} with reg {:?}", " -> first conflict {:?} with reg {:?}",
latest_first_conflict_point, first_conflict_point,
latest_first_conflict_reg first_conflict_reg
); );
// If we have already tried evictions once before and are // If we have already tried evictions once before and are
@@ -3053,8 +3077,8 @@ impl<'a, F: Function> Env<'a, F> {
} }
let bundle_start = self.bundles[bundle.index()].ranges[0].range.from; let bundle_start = self.bundles[bundle.index()].ranges[0].range.from;
split_at_point = std::cmp::max(latest_first_conflict_point, bundle_start); split_at_point = std::cmp::max(first_conflict_point, bundle_start);
requeue_with_reg = latest_first_conflict_reg; requeue_with_reg = first_conflict_reg;
// If the maximum spill weight in the conflicting-bundles set is >= this bundle's spill // If the maximum spill weight in the conflicting-bundles set is >= this bundle's spill
// weight, then don't evict. // weight, then don't evict.
@@ -3145,7 +3169,7 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!("trying bundle {:?} to preg {:?}", bundle, preg); log::debug!("trying bundle {:?} to preg {:?}", bundle, preg);
let preg_idx = PRegIndex::new(preg.index()); let preg_idx = PRegIndex::new(preg.index());
if let AllocRegResult::Allocated(_) = if let AllocRegResult::Allocated(_) =
self.try_to_allocate_bundle_to_reg(bundle, preg_idx) self.try_to_allocate_bundle_to_reg(bundle, preg_idx, None)
{ {
self.stats.spill_bundle_reg_success += 1; self.stats.spill_bundle_reg_success += 1;
success = true; success = true;