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:
- 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":
- 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
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:
- 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
@@ -611,6 +608,7 @@ enum AllocRegResult {
Allocated(Allocation),
Conflict(LiveBundleVec),
ConflictWithFixed,
ConflictHighCost,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -2425,9 +2423,14 @@ impl<'a, F: Function> Env<'a, F> {
&mut self,
bundle: LiveBundleIndex,
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 {
log::debug!("try_to_allocate_bundle_to_reg: {:?} -> {:?}", bundle, reg);
let mut conflicts = smallvec![];
let mut max_conflict_weight = 0;
// Traverse the BTreeMap in order by requesting the whole
// range spanned by the bundle and iterating over that
// 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);
if !conflicts.iter().any(|b| *b == 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 {
log::debug!(" -> conflict with fixed reservation");
@@ -2898,13 +2910,12 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!("attempt {}, req {:?}", attempts, req);
debug_assert!(attempts < 100 * self.func.insts());
let (conflicting_bundles, latest_first_conflict_point, latest_first_conflict_reg) =
match req {
let (conflicting_bundles, first_conflict_point, first_conflict_reg) = match req {
Requirement::Fixed(preg) => {
let preg_idx = PRegIndex::new(preg.index());
self.stats.process_bundle_reg_probes_fixed += 1;
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) => {
self.stats.process_bundle_reg_success_fixed += 1;
log::debug!(" -> allocated to fixed {:?}", preg_idx);
@@ -2931,13 +2942,15 @@ impl<'a, F: Function> Env<'a, F> {
PReg::invalid(),
)
}
AllocRegResult::ConflictHighCost => unreachable!(),
}
}
Requirement::Register(class) => {
// Scan all pregs and attempt to allocate.
let mut lowest_cost_conflict_set: Option<LiveBundleVec> = None;
let mut latest_first_conflict_point = ProgPoint::before(Inst::new(0));
let mut latest_first_conflict_reg = PReg::invalid();
let mut lowest_cost_conflict_cost: Option<u32> = None;
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
// 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;
let preg_idx = PRegIndex::new(preg.index());
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) => {
self.stats.process_bundle_reg_success_any += 1;
log::debug!(" -> allocated to any {:?}", preg_idx);
@@ -2976,25 +2994,31 @@ impl<'a, F: Function> Env<'a, F> {
let first_conflict_point =
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);
} else if self.maximum_spill_weight_in_bundle_set(&bundles)
< self.maximum_spill_weight_in_bundle_set(
lowest_cost_conflict_set.as_ref().unwrap(),
)
{
lowest_cost_conflict_point = first_conflict_point;
lowest_cost_conflict_reg = preg;
} else if cost < lowest_cost_conflict_cost.unwrap() {
lowest_cost_conflict_cost = Some(cost);
lowest_cost_conflict_set = Some(bundles);
lowest_cost_conflict_point = first_conflict_point;
lowest_cost_conflict_reg = preg;
}
}
AllocRegResult::ConflictWithFixed => {
log::debug!(" -> conflict with fixed alloc");
// 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.
(
lowest_cost_conflict_set.unwrap_or(smallvec![]),
latest_first_conflict_point,
latest_first_conflict_reg,
lowest_cost_conflict_point,
lowest_cost_conflict_reg,
)
}
@@ -3035,9 +3059,9 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!(" -> conflict set {:?}", conflicting_bundles);
log::debug!(
" -> latest first conflict {:?} with reg {:?}",
latest_first_conflict_point,
latest_first_conflict_reg
" -> first conflict {:?} with reg {:?}",
first_conflict_point,
first_conflict_reg
);
// 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;
split_at_point = std::cmp::max(latest_first_conflict_point, bundle_start);
requeue_with_reg = latest_first_conflict_reg;
split_at_point = std::cmp::max(first_conflict_point, bundle_start);
requeue_with_reg = first_conflict_reg;
// If the maximum spill weight in the conflicting-bundles set is >= this bundle's spill
// weight, then don't evict.
@@ -3145,7 +3169,7 @@ impl<'a, F: Function> Env<'a, F> {
log::debug!("trying bundle {:?} to preg {:?}", bundle, preg);
let preg_idx = PRegIndex::new(preg.index());
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;
success = true;